Mercurial > illumos > illumos-gate
changeset 10347:ef3957bb6db1
6853359 internal microphone always enabled in Lenovo T60
6872333 Audiohd should support MSI interrupt framework
author | Yang-Rong Jerry Zhou <Yangrong.Zhou@Sun.COM> |
---|---|
date | Thu, 20 Aug 2009 13:33:12 +0800 |
parents | 9f0b25e42dc5 |
children | 21d1dccbb74e |
files | usr/src/uts/common/io/audio/drv/audiohd/audiohd.c usr/src/uts/common/io/audio/drv/audiohd/audiohd.conf usr/src/uts/common/io/audio/drv/audiohd/audiohd.h |
diffstat | 3 files changed, 223 insertions(+), 26 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/common/io/audio/drv/audiohd/audiohd.c Wed Aug 19 17:19:28 2009 -0700 +++ b/usr/src/uts/common/io/audio/drv/audiohd/audiohd.c Thu Aug 20 13:33:12 2009 +0800 @@ -42,7 +42,7 @@ static int audiohd_suspend(audiohd_state_t *); /* interrupt handler */ -static uint_t audiohd_intr(caddr_t); +static uint_t audiohd_intr(caddr_t, caddr_t); /* * Local routines @@ -212,11 +212,159 @@ audio_dev_set_version(statep->adev, vers); } + +/* + * audiohd_add_intrs: + * + * Register FIXED or MSI interrupts. + */ +static int +audiohd_add_intrs(audiohd_state_t *statep, int intr_type) +{ + dev_info_t *dip = statep->hda_dip; + ddi_intr_handle_t ihandle; + int avail; + int actual; + int intr_size; + int count; + int i, j; + int ret; + + /* Get number of interrupts */ + ret = ddi_intr_get_nintrs(dip, intr_type, &count); + if ((ret != DDI_SUCCESS) || (count == 0)) { + audio_dev_warn(statep->adev, "ddi_intr_get_nintrs() failure," + "ret: %d, count: %d", ret, count); + return (DDI_FAILURE); + } + + /* Get number of available interrupts */ + ret = ddi_intr_get_navail(dip, intr_type, &avail); + if ((ret != DDI_SUCCESS) || (avail == 0)) { + audio_dev_warn(statep->adev, "ddi_intr_get_navail() failure, " + "ret: %d, avail: %d", ret, avail); + return (DDI_FAILURE); + } + + /* Allocate an array of interrupt handles */ + intr_size = count * sizeof (ddi_intr_handle_t); + statep->htable = kmem_alloc(intr_size, KM_SLEEP); + statep->intr_rqst = count; + + /* Call ddi_intr_alloc() */ + ret = ddi_intr_alloc(dip, statep->htable, intr_type, 0, + count, &actual, DDI_INTR_ALLOC_NORMAL); + if (ret != DDI_SUCCESS || actual == 0) { + audio_dev_warn(statep->adev, "ddi_intr_alloc() failed %d", + ret); + kmem_free(statep->htable, intr_size); + return (DDI_FAILURE); + } + if (actual < count) { + audio_dev_warn(statep->adev, "ddi_intr_alloc() Requested: %d," + "Received: %d", + count, actual); + } + statep->intr_cnt = actual; + + /* + * Get priority for first msi, assume remaining are all the same + */ + if ((ret = ddi_intr_get_pri(statep->htable[0], &statep->intr_pri)) != + DDI_SUCCESS) { + audio_dev_warn(statep->adev, "ddi_intr_get_pri() failed %d", + ret); + /* Free already allocated intr */ + for (i = 0; i < actual; i++) { + (void) ddi_intr_free(statep->htable[i]); + } + kmem_free(statep->htable, intr_size); + return (DDI_FAILURE); + } + + /* Test for high level mutex */ + if (statep->intr_pri >= ddi_intr_get_hilevel_pri()) { + audio_dev_warn(statep->adev, + "Hi level interrupt not supported"); + for (i = 0; i < actual; i++) + (void) ddi_intr_free(statep->htable[i]); + kmem_free(statep->htable, intr_size); + return (DDI_FAILURE); + } + + /* Call ddi_intr_add_handler() */ + for (i = 0; i < actual; i++) { + if ((ret = ddi_intr_add_handler(statep->htable[i], audiohd_intr, + (caddr_t)statep, (caddr_t)(uintptr_t)i)) != DDI_SUCCESS) { + audio_dev_warn(statep->adev, "ddi_intr_add_handler() " + "failed %d", ret); + /* Remove already added intr */ + for (j = 0; j < i; j++) { + ihandle = statep->htable[j]; + (void) ddi_intr_remove_handler(ihandle); + } + /* Free already allocated intr */ + for (i = 0; i < actual; i++) { + (void) ddi_intr_free(statep->htable[i]); + } + kmem_free(statep->htable, intr_size); + return (DDI_FAILURE); + } + } + + if ((ret = ddi_intr_get_cap(statep->htable[0], &statep->intr_cap)) + != DDI_SUCCESS) { + audio_dev_warn(statep->adev, + "ddi_intr_get_cap() failed %d", ret); + for (i = 0; i < actual; i++) { + (void) ddi_intr_remove_handler(statep->htable[i]); + (void) ddi_intr_free(statep->htable[i]); + } + kmem_free(statep->htable, intr_size); + return (DDI_FAILURE); + } + + return (DDI_SUCCESS); +} + +/* + * audiohd_rem_intrs: + * + * Unregister FIXED or MSI interrupts + */ +static void +audiohd_rem_intrs(audiohd_state_t *statep) +{ + + int i; + + /* Disable all interrupts */ + if (statep->intr_cap & DDI_INTR_FLAG_BLOCK) { + /* Call ddi_intr_block_disable() */ + (void) ddi_intr_block_disable(statep->htable, statep->intr_cnt); + } else { + for (i = 0; i < statep->intr_cnt; i++) { + (void) ddi_intr_disable(statep->htable[i]); + } + } + + /* Call ddi_intr_remove_handler() */ + for (i = 0; i < statep->intr_cnt; i++) { + (void) ddi_intr_remove_handler(statep->htable[i]); + (void) ddi_intr_free(statep->htable[i]); + } + + kmem_free(statep->htable, + statep->intr_rqst * sizeof (ddi_intr_handle_t)); +} + static int audiohd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { audiohd_state_t *statep; int instance; + int intr_types; + int i; instance = ddi_get_instance(dip); switch (cmd) { @@ -289,15 +437,60 @@ /* disable interrupts and clear interrupt status */ audiohd_disable_intr(statep); - /* set up the interrupt handler */ - if (ddi_add_intr(dip, 0, &statep->hda_intr_cookie, - (ddi_idevice_cookie_t *)NULL, audiohd_intr, (caddr_t)statep) != - DDI_SUCCESS) { + /* + * Get supported interrupt types + */ + if (ddi_intr_get_supported_types(dip, &intr_types) != DDI_SUCCESS) { audio_dev_warn(statep->adev, - "bad interrupt specification "); + "ddi_intr_get_supported_types failed"); goto error; } - statep->intr_added = B_TRUE; + + /* + * Add the h/w interrupt handler and initialise mutexes + */ + + if ((intr_types & DDI_INTR_TYPE_MSI) && statep->msi_enable) { + if (audiohd_add_intrs(statep, DDI_INTR_TYPE_MSI) != + DDI_SUCCESS) { + audio_dev_warn(statep->adev, "MSI registration failed, " + "trying FIXED interrupt type"); + } else { + statep->intr_type = DDI_INTR_TYPE_MSI; + statep->intr_added = B_TRUE; + } + } + if (!(statep->intr_added) && + (intr_types & DDI_INTR_TYPE_FIXED)) { + if (audiohd_add_intrs(statep, DDI_INTR_TYPE_FIXED) != + DDI_SUCCESS) { + audio_dev_warn(statep->adev, "FIXED interrupt " + "registration failed"); + goto error; + } + audio_dev_warn(statep->adev, "Using FIXED interrupt type"); + statep->intr_type = DDI_INTR_TYPE_FIXED; + statep->intr_added = B_TRUE; + } + if (!(statep->intr_added)) { + audio_dev_warn(statep->adev, "No interrupts registered"); + goto error; + } + mutex_init(&statep->hda_mutex, NULL, MUTEX_DRIVER, + DDI_INTR_PRI(statep->intr_pri)); + + /* + * Now that mutex lock is initialized, enable interrupts. + */ + if (statep->intr_cap & DDI_INTR_FLAG_BLOCK) { + /* Call ddi_intr_block_enable() for MSI interrupts */ + (void) ddi_intr_block_enable(statep->htable, statep->intr_cnt); + } else { + /* Call ddi_intr_enable for MSI or FIXED interrupts */ + for (i = 0; i < statep->intr_cnt; i++) { + (void) ddi_intr_enable(statep->htable[i]); + } + } /* * Register audio controls. @@ -449,14 +642,12 @@ static void audiohd_destroy(audiohd_state_t *statep) { - dev_info_t *dip = statep->hda_dip; - mutex_enter(&statep->hda_mutex); audiohd_stop_dma(statep); audiohd_disable_intr(statep); mutex_exit(&statep->hda_mutex); if (statep->intr_added) { - ddi_remove_intr(dip, 0, statep->hda_intr_cookie); + audiohd_rem_intrs(statep); } if (statep->hda_ksp) kstat_delete(statep->hda_ksp); @@ -2143,20 +2334,13 @@ } statep->adev = adev; statep->intr_added = B_FALSE; + statep->msi_enable = ddi_prop_get_int(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "msi_enable", B_TRUE); /* set device information */ audio_dev_set_description(adev, AUDIOHD_DEV_CONFIG); audio_dev_set_version(adev, AUDIOHD_DEV_VERSION); - if (ddi_get_iblock_cookie(dip, (uint_t)0, &statep->hda_intr_cookie) != - DDI_SUCCESS) { - audio_dev_warn(statep->adev, - "cannot get iblock cookie"); - return (DDI_FAILURE); - } - mutex_init(&statep->hda_mutex, NULL, - MUTEX_DRIVER, statep->hda_intr_cookie); - statep->hda_rirb_rp = 0; return (DDI_SUCCESS); @@ -3531,12 +3715,14 @@ uint8_t mixer_allow = 1; /* - * work around for laptops which have IDT audio chipset, such as - * HP mini 1000 laptop, Dell Lattitude 6400. We don't allow mixer - * widget on such path, which leads to speaker loud hiss noise. + * Work around for laptops which have IDT or AD audio chipset, such as + * HP mini 1000 laptop, Dell Lattitude 6400, Lenovo T60. We don't + * allow mixer widget on such path, which leads to speaker + * loud hiss noise. */ if (codec->vid == AUDIOHD_CODEC_IDT7608 || - codec->vid == AUDIOHD_CODEC_IDT76B2) + codec->vid == AUDIOHD_CODEC_IDT76B2 || + codec->vid == AUDIOHD_CODEC_AD1981) mixer_allow = 0; /* search an exclusive mixer widget path. This is preferred */ audiohd_do_build_output_path(codec, mixer_allow, &mnum, 1, 0); @@ -5766,14 +5952,16 @@ * DDI_INTR_UNCLAIMED Interrupt not claimed, and thus ignored */ static uint_t -audiohd_intr(caddr_t arg) +audiohd_intr(caddr_t arg1, caddr_t arg2) { - audiohd_state_t *statep = (audiohd_state_t *)arg; + audiohd_state_t *statep = (void *)arg1; uint32_t status; uint32_t regbase; uint32_t resp, respex; uint8_t sdstatus, rirbsts; int i, ret; + + _NOTE(ARGUNUSED(arg2)) audio_engine_t *do_adc = NULL; audio_engine_t *do_dac = NULL;
--- a/usr/src/uts/common/io/audio/drv/audiohd/audiohd.conf Wed Aug 19 17:19:28 2009 -0700 +++ b/usr/src/uts/common/io/audio/drv/audiohd/audiohd.conf Thu Aug 20 13:33:12 2009 +0800 @@ -46,3 +46,4 @@ play-interrupts=175; record-interrupts=175; audiohd_beep=1; +msi_enable=1;
--- a/usr/src/uts/common/io/audio/drv/audiohd/audiohd.h Wed Aug 19 17:19:28 2009 -0700 +++ b/usr/src/uts/common/io/audio/drv/audiohd/audiohd.h Thu Aug 20 13:33:12 2009 +0800 @@ -42,6 +42,7 @@ */ #define AUDIOHD_CODEC_IDT7608 0x111d7608 #define AUDIOHD_CODEC_IDT76B2 0x111d76b2 +#define AUDIOHD_CODEC_AD1981 0x11d41981 #define AUDIOHD_CODECID_ALC888 0x10ec0888 #define AUDIOHD_CODECID_SONY1 0x10ec0260 #define AUDIOHD_CODECID_SONY2 0x10ec0262 @@ -789,7 +790,14 @@ caddr_t hda_reg_base; ddi_acc_handle_t hda_pci_handle; ddi_acc_handle_t hda_reg_handle; - ddi_iblock_cookie_t hda_intr_cookie; + + ddi_intr_handle_t *htable; /* For array of interrupts */ + int intr_type; /* What type of interrupt */ + int intr_rqst; /* # of request intrs count */ + int intr_cnt; /* # of intrs count returned */ + uint_t intr_pri; /* Interrupt priority */ + int intr_cap; /* Interrupt capabilities */ + boolean_t msi_enable; audiohd_dma_t hda_dma_corb; audiohd_dma_t hda_dma_rirb;