Mercurial > illumos > illumos-gate
changeset 12976:d106a535d961
6929250 audiohd driver lack of recording loopback function
author | Zhao Edgar Liu - Sun Microsystems <Edgar.Liu@Sun.COM> |
---|---|
date | Fri, 30 Jul 2010 10:59:02 +0800 |
parents | 6bd5885dca7c |
children | b65a8427f8fe |
files | usr/src/uts/common/io/audio/drv/audiohd/audiohd.c usr/src/uts/common/io/audio/drv/audiohd/audiohd.h usr/src/uts/common/io/warlock/audiohd.wlcmd |
diffstat | 3 files changed, 260 insertions(+), 35 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/common/io/audio/drv/audiohd/audiohd.c Thu Jul 29 18:49:19 2010 -0700 +++ b/usr/src/uts/common/io/audio/drv/audiohd/audiohd.c Fri Jul 30 10:59:02 2010 +0800 @@ -33,7 +33,6 @@ /* * Module linkage routines for the kernel */ - static int audiohd_attach(dev_info_t *, ddi_attach_cmd_t); static int audiohd_detach(dev_info_t *, ddi_detach_cmd_t); static int audiohd_quiesce(dev_info_t *); @@ -326,8 +325,7 @@ /* interrupt cookie and initialize mutex */ if (audiohd_init_state(statep, dip) != DDI_SUCCESS) { - cmn_err(CE_WARN, - "audiohd_init_state failed"); + audio_dev_warn(NULL, "audiohd_init_state failed"); goto error; } @@ -1123,9 +1121,9 @@ } for (i = 0; i < statep->pathnum; i++) { - path = statep->path[i]; - if (!path) + if ((path = statep->path[i]) == NULL) continue; + codec = path->codec; for (j = 0; j < path->pin_nums; j++) { wid = path->pin_wid[j]; @@ -1219,8 +1217,8 @@ val = statep->ctrls[CTL_RECSRC].val; set_val = ddi_ffs(val & 0xffff) - 1; for (i = 0; i < statep->pathnum; i++) { - path = statep->path[i]; - if (path == NULL || path->path_type != RECORD) + if ((path = statep->path[i]) == NULL || + path->path_type != RECORD) continue; switch (set_val) { @@ -1231,31 +1229,32 @@ wid = path->pin_wid[j]; widget = path->codec->widget[wid]; pin = (audiohd_pin_t *)widget->priv; + if ((1U << pin->device) == val) { AUDIOHD_ENABLE_PIN_IN(statep, - path->codec->index, - pin->wid); + path->codec->index, pin->wid); pin_wid = pin->wid; codec = path->codec; statep->in_port = pin->device; } else if (statep->in_port == pin->device) { AUDIOHD_DISABLE_PIN_IN(statep, - path->codec->index, - pin->wid); + path->codec->index, pin->wid); } } break; default: break; } - break; } + if (pin_wid == 0) return (DDI_SUCCESS); + w = codec->widget[pin_wid]; pin = (audiohd_pin_t *)w->priv; - w = codec->widget[pin->adc_dac_wid]; + w = codec->widget[pin->adc_wid]; path = (audiohd_path_t *)w->priv; + /* * If there is a real selector in this input path, * we select the right one input for the selector. @@ -1263,15 +1262,18 @@ if (path->sum_wid) { w = codec->widget[path->sum_wid]; if (w->type == WTYPE_AUDIO_SEL) { - for (i = 0; i < path->pin_nums; i++) - if (path->pin_wid[i] == pin_wid) + for (i = 0; i < path->pin_nums; i++) { + if (path->pin_wid[i] == pin->wid) { + (void) audioha_codec_verb_get( + statep, codec->index, path->sum_wid, + AUDIOHDC_VERB_SET_CONN_SEL, + path->sum_selconn[i]); break; - (void) audioha_codec_verb_get( - statep, codec->index, path->sum_wid, - AUDIOHDC_VERB_SET_CONN_SEL, - path->sum_selconn[i]); + } + } } } + return (DDI_SUCCESS); } @@ -1599,6 +1601,7 @@ return (0); } + static int audiohd_set_headphone(void *arg, uint64_t val) { @@ -1613,6 +1616,7 @@ return (0); } + static int audiohd_set_linein(void *arg, uint64_t val) { @@ -1629,6 +1633,68 @@ } static int +audiohd_set_loopback(void *arg, uint64_t val) +{ + audiohd_ctrl_t *pc = arg; + audiohd_state_t *statep = pc->statep; + audiohd_path_t *path = NULL; + audiohd_widget_t *widget = NULL; + audiohd_pin_t *pin = NULL; + wid_t wid; + uint32_t pinctrl; + int i, j; + + mutex_enter(&statep->hda_mutex); + pc->val = val; + + for (i = 0; i < statep->pathnum; i++) { + path = statep->path[i]; + if (path == NULL || path->path_type != LOOPBACK) + continue; + + for (j = 0; j < path->pin_nums; j++) { + wid = path->pin_wid[j]; + widget = path->codec->widget[wid]; + pin = (audiohd_pin_t *)widget->priv; + + if (val == 1) { + /* Turn on loopback recording */ + pinctrl = audioha_codec_verb_get(statep, + path->codec->index, wid, + AUDIOHDC_VERB_GET_PIN_CTRL, 0); + (void) audioha_codec_verb_get(statep, + path->codec->index, wid, + AUDIOHDC_VERB_SET_PIN_CTRL, + pinctrl | AUDIOHD_PIN_OUT_ENABLE); + + if (pin->cap & AUDIOHD_EXT_AMP_MASK) { + (void) audioha_codec_verb_get(statep, + path->codec->index, + wid, AUDIOHDC_VERB_SET_EAPD, + AUDIOHD_EXT_AMP_ENABLE); + } + + } else { + /* Turn off loopback recording */ + if (pin->device == DTYPE_LINE_IN) { + pinctrl = audioha_codec_verb_get(statep, + path->codec->index, wid, + AUDIOHDC_VERB_GET_PIN_CTRL, 0); + (void) audioha_codec_verb_get(statep, + path->codec->index, wid, + AUDIOHDC_VERB_SET_PIN_CTRL, + pinctrl & ~AUDIOHD_PIN_OUT_ENABLE); + } + } + + } + } + mutex_exit(&statep->hda_mutex); + + return (0); +} + +static int audiohd_set_mic(void *arg, uint64_t val) { audiohd_ctrl_t *pc = arg; @@ -1695,7 +1761,6 @@ #define MONVOL (MONCTL | AUDIO_CTRL_FLAG_MONVOL) #define MAINVOL (PLAYCTL | AUDIO_CTRL_FLAG_MAINVOL) #define RECVOL (RECCTL | AUDIO_CTRL_FLAG_RECVOL) -#define RWCTL AUDIO_CTRL_FLAG_RW static void audiohd_del_controls(audiohd_state_t *statep) @@ -1759,6 +1824,30 @@ } static void +audiohd_create_bool(audiohd_state_t *statep, int ctl, + const char *id, int defval, audio_ctrl_wr_t fn) +{ + audiohd_ctrl_t *ac; + audio_ctrl_desc_t desc; + + bzero(&desc, sizeof (desc)); + + ac = &statep->ctrls[ctl]; + ac->statep = statep; + ac->num = ctl; + + desc.acd_name = id; + desc.acd_type = AUDIO_CTRL_TYPE_BOOLEAN; + desc.acd_minvalue = 0; + desc.acd_maxvalue = 1; + desc.acd_flags = RECCTL; + + ac->val = defval; + ac->ctrl = audio_dev_add_control(statep->adev, &desc, + audiohd_get_control, fn, ac); +} + +static void audiohd_create_recsrc(audiohd_state_t *statep) { audiohd_ctrl_t *ac; @@ -1772,7 +1861,7 @@ desc.acd_name = AUDIO_CTRL_ID_RECSRC; desc.acd_type = AUDIO_CTRL_TYPE_ENUM; - desc.acd_flags = RECCTL; + desc.acd_flags = RECVOL; desc.acd_minvalue = statep->inmask; desc.acd_maxvalue = statep->inmask; for (int i = 0; audiohd_dtypes[i]; i++) { @@ -1863,19 +1952,24 @@ if (widget->type == WTYPE_BEEP && path->gain_wid != 0) { audiohd_create_mono(statep, CTL_BEEP, - AUDIO_CTRL_ID_BEEP, RWCTL, 75, + AUDIO_CTRL_ID_BEEP, AUDIO_CTRL_FLAG_RW, 75, audiohd_set_beep); continue; } } } - if (!statep->monitor_unsupported) { + if (statep->monitor_supported) { audiohd_create_stereo(statep, CTL_MONGAIN, AUDIO_CTRL_ID_MONGAIN, MONVOL, 0, audiohd_set_mongain); } + if (statep->loopback_supported) { + audiohd_create_bool(statep, CTL_LOOP, AUDIO_CTRL_ID_LOOPBACK, + 0, audiohd_set_loopback); + } + audiohd_create_recsrc(statep); audiohd_configure_output(statep); audiohd_configure_input(statep); @@ -1990,8 +2084,7 @@ statep->hda_rirb_rp = 0; if ((adev = audio_dev_alloc(dip, 0)) == NULL) { - cmn_err(CE_WARN, - "unable to allocate audio dev"); + audio_dev_warn(NULL, "unable to allocate audio dev"); return (DDI_FAILURE); } statep->adev = adev; @@ -2684,6 +2777,7 @@ widget->output_path_next = AUDIOHD_NULL_CONN; widget->input_path_next = AUDIOHD_NULL_CONN; widget->beep_path_next = AUDIOHD_NULL_CONN; + widget->loopback_path_next = AUDIOHD_NULL_CONN; widcap = audioha_codec_verb_get(statep, caddr, wid, AUDIOHDC_VERB_GET_PARAM, AUDIOHDC_PAR_AUDIO_WID_CAP); @@ -3177,7 +3271,7 @@ path->statep = statep; wdac = codec->widget[wid]; wdac->priv = path; - pin->adc_dac_wid = wid; + pin->dac_wid = wid; pin->finish = 1; widget->path_flags |= AUDIOHD_PATH_DAC; widget->out_weight++; @@ -3555,7 +3649,7 @@ widget->path_flags |= AUDIOHD_PATH_ADC; widget->in_weight++; path->pin_wid[path->pin_nums++] = wid; - pin->adc_dac_wid = path->adda_wid; + pin->adc_wid = path->adda_wid; return (DDI_SUCCESS); } break; @@ -4196,9 +4290,9 @@ } if (mixernum == 0) - statep->monitor_unsupported = B_TRUE; + statep->monitor_supported = B_FALSE; else - statep->monitor_unsupported = B_FALSE; + statep->monitor_supported = B_TRUE; } /* audiohd_build_monitor_path */ /* @@ -4699,6 +4793,128 @@ } } /* audiohd_finish_beep_path */ +static int +audiohd_find_output_pins(hda_codec_t *codec, wid_t wid, int depth, + audiohd_path_t *path) +{ + audiohd_widget_t *widget = codec->widget[wid]; + audiohd_pin_t *pin = NULL; + int num, retval = (DDI_FAILURE); + + if (depth > AUDIOHD_MAX_DEPTH) + return (retval); + if (widget == NULL) + return (retval); + + switch (widget->type) { + case WTYPE_PIN: + pin = (audiohd_pin_t *)widget->priv; + if (pin->no_phys_conn) + return (DDI_FAILURE); + + switch (pin->device) { + case DTYPE_LINE_IN: + /* Connection between line-in and output pins */ + path->pin_wid[path->pin_nums++] = wid; + break; + case DTYPE_LINEOUT: + case DTYPE_HP_OUT: + case DTYPE_SPDIF_OUT: + widget->path_flags |= AUDIOHD_PATH_LOOPBACK; + widget->in_weight++; + pin->adc_wid = path->adda_wid; + path->pin_wid[path->pin_nums++] = wid; + retval = (DDI_SUCCESS); + break; + default: + break; + } + break; + case WTYPE_AUDIO_MIX: + case WTYPE_AUDIO_SEL: + /* + * If the sum widget has only one input, we don't + * consider it as a real sum widget. + */ + if (widget->nconns == 1) { + widget->loopback_path_next = 0; + retval = audiohd_find_output_pins(codec, + widget->avail_conn[0], depth + 1, path); + if (retval == (DDI_SUCCESS)) { + widget->path_flags |= AUDIOHD_PATH_LOOPBACK; + widget->in_weight++; + } + break; + } + + for (int i = 0; i < widget->nconns; i++) { + retval = audiohd_find_output_pins(codec, + widget->avail_conn[i], depth + 1, path); + if (retval == (DDI_SUCCESS)) { + widget->loopback_path_next = i; + widget->in_weight++; + num = path->pin_nums - 1; + path->sum_selconn[num] = i; + path->sum_wid = wid; + widget->path_flags |= AUDIOHD_PATH_LOOPBACK; + break; + } + } + break; + default: + break; + } + + return (retval); +} + +static void +audiohd_build_loopback_path(hda_codec_t *codec) +{ + audiohd_state_t *statep = codec->statep; + audiohd_widget_t *widget; + audiohd_path_t *path = NULL; + wid_t wid; + int i, retval; + uint8_t rtag = 0; + + for (wid = codec->first_wid; wid <= codec->last_wid; wid++) { + widget = codec->widget[wid]; + + /* check if it is an ADC widget */ + if (widget == NULL || widget->type != WTYPE_AUDIO_IN) + continue; + + if (path == NULL) + path = kmem_zalloc(sizeof (audiohd_path_t), KM_SLEEP); + else + bzero(path, sizeof (audiohd_port_t)); + path->adda_wid = wid; + + for (i = 0; i < widget->nconns; i++) { + retval = audiohd_find_output_pins(codec, + widget->avail_conn[i], 0, path); + if (retval == (DDI_SUCCESS)) { + path->codec = codec; + path->statep = statep; + path->path_type = LOOPBACK; + path->tag = ++rtag; + codec->nistream++; + statep->path[statep->pathnum++] = path; + widget->loopback_path_next = i; + widget->priv = path; + path = NULL; + statep->loopback_supported = B_TRUE; + break; + } + } + } + + + if (path) + kmem_free(path, sizeof (audiohd_path_t)); +} /* audiohd_build_loopback_path() */ + /* * audiohd_build_path() * @@ -4729,6 +4945,8 @@ audiohd_build_beep_path(statep->codec[i]); audiohd_build_beep_amp(statep->codec[i]); audiohd_finish_beep_path(statep->codec[i]); + + audiohd_build_loopback_path(statep->codec[i]); } } } /* audiohd_build_path */ @@ -5033,6 +5251,7 @@ audiohd_finish_output_path(statep->codec[i]); audiohd_finish_input_path(statep->codec[i]); audiohd_finish_monitor_path(statep->codec[i]); + audiohd_finish_beep_path(statep->codec[i]); } }
--- a/usr/src/uts/common/io/audio/drv/audiohd/audiohd.h Thu Jul 29 18:49:19 2010 -0700 +++ b/usr/src/uts/common/io/audio/drv/audiohd/audiohd.h Fri Jul 30 10:59:02 2010 +0800 @@ -550,6 +550,7 @@ #define AUDIOHD_PATH_MON (1 << 2) #define AUDIOHD_PATH_NOMON (1 << 3) #define AUDIOHD_PATH_BEEP (1 << 4) +#define AUDIOHD_PATH_LOOPBACK (1 << 5) typedef struct audiohd_path audiohd_path_t; typedef struct audiohd_widget audiohd_widget_t; @@ -606,13 +607,14 @@ wid_t monitor_path_next[AUDIOHD_MAX_CONN]; /* output pin -> input pin */ wid_t beep_path_next; /* output pin -> beep widget */ + wid_t loopback_path_next; /* ADC -> output pin */ uint16_t used; /* * pointer to struct depending on widget type: - * 1. DAC audiohd_ostream_t - * 2. ADC audiohd_istream_t + * 1. DAC audiohd_path_t + * 2. ADC audiohd_path_t * 3. PIN audiohd_pin_t */ void *priv; @@ -633,6 +635,7 @@ PLAY = 0, RECORD = 1, BEEP = 2, + LOOPBACK = 3, } path_type_t; struct audiohd_path { @@ -710,6 +713,7 @@ CTL_MONSRC, CTL_RECSRC, CTL_BEEP, + CTL_LOOP, /* this one must be last */ CTL_MAX @@ -739,7 +743,8 @@ uint32_t ctrl; uint32_t assoc; uint32_t seq; - wid_t adc_dac_wid; /* AD/DA wid which can route to this pin */ + wid_t adc_wid; + wid_t dac_wid; wid_t beep_wid; int no_phys_conn; enum audiohda_device_type device; @@ -853,8 +858,9 @@ /* * Controls */ - audiohd_ctrl_t ctrls[CTL_MAX]; - boolean_t monitor_unsupported; + audiohd_ctrl_t ctrls[CTL_MAX]; + boolean_t monitor_supported; + boolean_t loopback_supported; /* for multichannel */ uint8_t chann[AUDIOHD_MAX_ASSOC]; @@ -914,7 +920,6 @@ AUDIOHDC_VERB_SET_PIN_CTRL, AUDIOHDC_PIN_CONTROL_IN_ENABLE | 4); \ } - /* * disable input pin */
--- a/usr/src/uts/common/io/warlock/audiohd.wlcmd Thu Jul 29 18:49:19 2010 -0700 +++ b/usr/src/uts/common/io/warlock/audiohd.wlcmd Fri Jul 30 10:59:02 2010 +0800 @@ -53,6 +53,7 @@ root audiohd_set_speaker root audiohd_set_surround root audiohd_set_mongain +root audiohd_set_loopback root audiohd_beep_on root audiohd_beep_off root audiohd_beep_freq