static int __devinit aic3008_probe(struct snd_soc_codec *codec) { AUD_INFO("aic3008_probe() start... aic3008_codec:%p", codec); int ret = 0; struct aic3008_priv *aic3008 = snd_soc_codec_get_drvdata(codec); aic3008->codec = codec; aic3008->codec->control_data = (void *)codec_spi_dev; if (!aic3008) { AUD_ERR("%s: Codec not registered, SPI device not yet probed\n", &aic3008->codec->name); return -ENODEV; } aic3008_sw_reset(codec); // local call to reset codec aic3008_set_bias_level(codec, SND_SOC_BIAS_STANDBY); // request space for SPI commands data of AIC3008 aic3008_uplink = init_2d_array(IO_CTL_ROW_MAX, IO_CTL_COL_MAX); aic3008_downlink = init_2d_array(IO_CTL_ROW_MAX, IO_CTL_COL_MAX); aic3008_minidsp = init_2d_array(MINIDSP_ROW_MAX, MINIDSP_COL_MAX); bulk_tx = kcalloc(MINIDSP_COL_MAX * 2 , sizeof(uint8_t), GFP_KERNEL); AUD_INFO("Audio Codec Driver init complete in %lld ms\n", ktime_to_ms(ktime_get()) - drv_up_time); return ret; }
int route_tx_enable(int path, int en) { AUD_DBG("[TX] (%d, %d) uses aic3008 default TX setting\n", path, en); if (en) { /* Uplink_Wakeup */ AUD_INFO("[TX] route_tx_enable call Uplink_Wakeup"); aic3008_config(CODEC_UPLINK_ON, ARRAY_SIZE(CODEC_UPLINK_ON)); /* Path switching */ switch (path) { case CALL_UPLINK_IMIC_RECEIVER: case CALL_UPLINK_IMIC_HEADPHONE: case CALL_UPLINK_IMIC_SPEAKER: case VOICERECORD_IMIC: /* By pass */ AUD_INFO("[TX] route_tx_enable call MECHA_UPLINK_IMIC"); aic3008_config(MECHA_UPLINK_IMIC, ARRAY_SIZE(MECHA_UPLINK_IMIC)); break; case CALL_UPLINK_EMIC_HEADPHONE: case VOICERECORD_EMIC: AUD_INFO("[TX] route_tx_enable call UPLINK_EMIC,"); aic3008_config(UPLINK_EMIC, ARRAY_SIZE(UPLINK_EMIC)); break; } } else { /* Uplink_Off */ AUD_INFO("[TX] route_tx_enable call CODEC_UPLINK_OFF"); aic3008_config(CODEC_UPLINK_OFF, ARRAY_SIZE(CODEC_UPLINK_OFF)); } return 0; }
void power_config(const char *name, int pin, int method) { int ret = 0; switch (method) { case REGULATOR_METHOD: audio_regulator = regulator_get(NULL, name); if (IS_ERR_OR_NULL(audio_regulator)) { AUD_ERR("[PWR] couldn't get regulator %s, pin = %d, addr = %p.\n", name, pin, &audio_regulator); return; } ret = regulator_is_enabled(audio_regulator); if (ret > 0) { AUD_DBG("[PWR] regulator %s was enabled, pin = %d.\n", name, pin); return; } else if (ret < 0) { AUD_ERR("[PWR] regulator_is_enable error.\n"); return; } ret = regulator_enable(audio_regulator); if (ret < 0) { AUD_ERR("[PWR] couldn't enable regulator %s, pin = %d, ret = %d.\n", name, pin, ret); } AUD_INFO("[PWR] ***** regulator %s %d enable *****\n", name, pin); break; case GPIO_OUTPUT: gpio_direction_output(pin, 1); tegra_gpio_enable(pin); gpio_set_value(pin, 1); AUD_INFO("[PWR] ***** gpio %s %d output enable *****\n", name, pin); break; case GPIO_INPUT: gpio_direction_input(pin); tegra_gpio_enable(pin); AUD_INFO("[PWR] ***** gpio %s %d input enable *****\n", name, pin); break; case INIT_OUTPUT_LOW: gpio_request(pin, name); gpio_direction_output(pin, 0); break; case INIT_OUTPUT_HIGH: gpio_request(pin, name); gpio_direction_output(pin, 1); break; case INIT_INPUT: gpio_request(pin, name); gpio_direction_input(pin); break; default: AUD_ERR("[PWR] ***** power_configure nothing *****\n"); } return; }
static __devinit int tegra_aic3008_driver_probe(struct platform_device *pdev) { struct snd_soc_card *card = &snd_soc_tegra_aic3008; struct tegra_aic3008 *machine; struct htc_asoc_platform_data *pdata; int ret; AUD_INFO("starting tegra_aic3008_driver_probe...\n"); pdata = pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, "No platform data supplied\n"); return -EINVAL; } AUD_INFO("starting tegra_aic3008_driver_probe...%p %p\n", &pdata->aic3008_power, &aic3008_power_ctl); aic3008_power_ctl = &pdata->aic3008_power; machine = kzalloc(sizeof(struct tegra_aic3008), GFP_KERNEL); if (!machine) { AUD_ERR("Can't allocate tegra_aic3008 struct\n"); return -ENOMEM; } machine->pdata = pdata; ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev, card); util_data = &machine->util_data; if (ret) goto err_free_machine; AUD_DBG("DONE tegra_asoc_utils_init()\n"); card->dev = &pdev->dev; platform_set_drvdata(pdev, card); snd_soc_card_set_drvdata(card, machine); ret = snd_soc_register_card(card); if (ret) { AUD_ERR("snd_soc_register_card failed %d\n",ret); goto err_unregister_switch; } AUD_DBG("DONE snd_soc_register_card()\n"); return 0; err_unregister_switch: tegra_asoc_utils_fini(&machine->util_data); err_free_machine: kfree(machine); return ret; }
/* * This function arranges the address and data bytes in a large command list * and use kernel SPI API, * i.e. use single spi_write() to write spi commands to the audio codec. */ static int32_t spi_read_list(CODEC_SPI_CMD *cmds, int num) { int i; int rc; unsigned char page = 0; unsigned char buffer[2] = { 0, 0 }; unsigned char result[2] = { 0, 0 }; if (!codec_spi_dev) return 0; codec_spi_dev->bits_per_word = 16; for (i = 0; i < num; i++) { udelay(20); /* if writing page, then don't read its value */ if(i==2) { AUD_INFO("Skip software reset cmd when dump spi! %02X\n", cmds[i].reg); continue; } if (cmds[i].reg == 0x00) { buffer[1] = cmds[i].reg << 1; buffer[0] = cmds[i].data; rc = spi_write(codec_spi_dev, buffer, sizeof(buffer)); if (rc < 0) { return rc; } AUD_INFO("====== write page %02X ======", buffer[0]); page = buffer[0]; } else if (cmds[i].reg == 0x7F && page == 0x00) { buffer[1] = cmds[i].reg << 1; buffer[0] = cmds[i].data; rc = spi_write(codec_spi_dev, buffer, sizeof(buffer)); if (rc < 0) { return rc; } AUD_INFO("====== write book %02X ======", buffer[0]); } else { buffer[1] = cmds[i].reg << 1 | 1; rc = spi_write_then_read(codec_spi_dev, buffer, 2, result, 2); if (rc < 0) return rc; AUD_INFO("read: reg: 0x%02X , data: 0x%02X\n", cmds[i].reg, result[0]); } } return 0; }
static void aic3008_rx_config(int mode) { /* use default setting when rx table doesn't be updated*/ if (aic3008_downlink == NULL) { AUD_DBG("[RX] use default setting since rx table doesn't be updated"); if (mode == DOWNLINK_PATH_OFF) route_rx_enable(mode, 0); else route_rx_enable(mode, 1); return; } if (mode != DOWNLINK_PATH_OFF) { /* Downlink Wakeup */ AUD_DBG("[RX] ----- downlink wakeup len(%d) -----\n", (aic3008_downlink[DOWNLINK_WAKEUP][0].data-1)); aic3008_config(&aic3008_downlink[DOWNLINK_WAKEUP][1], aic3008_downlink[DOWNLINK_WAKEUP][0].data); } /* route rx device */ AUD_INFO("[RX] ----- change i/o downlink RX %d len(%d) -----\n", mode, (aic3008_downlink[mode][0].data-1)); aic3008_config(&aic3008_downlink[mode][1], aic3008_downlink[mode][0].data); }
static void aic3008_tx_config(int mode) { /* mode = 0 for initialisation */ /* use default setting when tx table doesn't be updated*/ if (aic3008_uplink == NULL) { AUD_DBG("[TX] use default setting since tx table doesn't be updated"); if (mode == UPLINK_PATH_OFF) route_tx_enable(mode, 0); /* uploink off */ else route_tx_enable(mode, 1); /* if no mem for aic3008_uplink + on */ return; } /* if not uplink off or power off */ if (mode != UPLINK_PATH_OFF && mode != POWER_OFF) { /* uplink_Wakeup */ AUD_DBG("[TX] ----- uplink wakeup len(%d) -----\n", (aic3008_uplink[UPLINK_WAKEUP][0].data-1)); aic3008_config(&aic3008_uplink[UPLINK_WAKEUP][1], aic3008_uplink[UPLINK_WAKEUP][0].data); } /* route tx device */ AUD_INFO("[TX] ----- change i/o uplink TX %d len(%d) -----\n", mode, (aic3008_uplink[mode][0].data-1)); aic3008_config(&aic3008_uplink[mode][1], aic3008_uplink[mode][0].data); }
/* Access function pointed by ctl_ops to call control operations */ static int aic3008_config(CODEC_SPI_CMD *cmds, int size) { int i, retry, ret; unsigned char data; if(!aic3008_power_ctl->isPowerOn) { AUD_INFO("aic3008_config: AIC3008 is power off now"); return -EINVAL; } if (!codec_spi_dev) { AUD_ERR("no spi device\n"); return -EFAULT; } if (cmds == NULL) { AUD_ERR("invalid spi parameters\n"); return -EINVAL; } /* large dsp image use bulk mode to transfer */ if (size < 1000) { for (i = 0; i < size; i++) { switch (cmds[i].act) { case 'w': codec_spi_write(cmds[i].reg, cmds[i].data, true); break; case 'r': for (retry = AIC3008_MAX_RETRY; retry > 0; retry--) { ret = codec_spi_read(cmds[i].reg, &data, true); if (ret < 0) { AUD_ERR("read fail %d, retry\n", ret); hr_msleep(1); } else if (data == cmds[i].data) { AUD_DBG("data == cmds\n"); break; } } if (retry <= 0) AUD_DBG("3008 power down procedure," " flag 0x%02X=0x%02X(0x%02X)\n", cmds[i].reg, ret, cmds[i].data); break; case 'd': msleep(cmds[i].data); break; default: break; } } } else { /* use bulk to transfer large data */ spi_write_table_parsepage(cmds, size); AUD_DBG("Here is bulk mode\n"); } return 0; }
static void aic3008_powerup(void) { int64_t t1, t2; t1 = ktime_to_ms(ktime_get()); aic3008_power_ctl->resume(); t2 = ktime_to_ms(ktime_get()) - t1; AUD_INFO("[PWR] power on AIC3008 %lldms\n", t2); return; }
void power_deconfig(const char *name, int pin, int method) { int ret = 0; switch (method) { case REGULATOR_METHOD: audio_regulator = regulator_get(NULL, name); if (IS_ERR_OR_NULL(audio_regulator)) { AUD_ERR("[PWR] couldn't get regulator %s %d, addr = %p.\n", name, pin, &audio_regulator); return; } ret = regulator_is_enabled(audio_regulator); if (ret == 0) { AUD_DBG("[PWR] regulator %s was disabled, pin = %d.\n", name, pin); return; } else if (ret < 0) { AUD_ERR("[PWR] regulator_is_enable error.\n"); return; } ret = regulator_disable(audio_regulator); if (ret < 0) { AUD_ERR("[PWR] couldn't enable regulator %s %d, ret = %d.\n", name, pin, ret); } AUD_INFO("[PWR] ***** regulator %s %d disable *****\n", name, pin); break; case GPIO_OUTPUT: gpio_set_value(pin, 0); AUD_INFO("[PWR] ***** gpio %s %d disable *****\n", name, pin); break; default: AUD_ERR("[PWR] ***** power_deconfig nothing *****\n"); } return; }
void dock_config(const char *name, int pin, bool output, bool out_val) { int ret = 0; if (output) { ret = gpio_direction_output(pin, out_val); if (ret < 0) { AUD_ERR("[PWR] set %s %d output direction failed\n", name, pin); return; } tegra_gpio_enable(pin); AUD_INFO("[PWR] ***** gpio %s %d enable *****\n", name, pin); } else { ret = gpio_direction_input(pin); if (ret < 0) { AUD_ERR("[PWR] set %s %d input direction failed\n", name, pin); return; } tegra_gpio_enable(pin); AUD_INFO("[PWR] ***** gpio %s %d disable *****\n", name, pin); } return; }
void aic3008_set_mic_bias(int on) { if (on) { AUD_INFO("[PWR] enalbe_AUD_HPMIC_BIAS\n"); if (!aic3008_power_ctl->isPowerOn) { AUD_INFO("[PWR] codec was power off wakeup first\n"); aic3008_powerup(); aic3008_config(ENABLE_AUD_HPMIC_EXT, ARRAY_SIZE(ENABLE_AUD_HPMIC_EXT)); aic3008_powerdown(); } else aic3008_config(ENABLE_AUD_HPMIC_EXT, ARRAY_SIZE(ENABLE_AUD_HPMIC_EXT)); } else { AUD_INFO("[PWR] disalbe_AUD_HPMIC_BIAS\n"); if (aic3008_tx_mode == CALL_UPLINK_IMIC_SPEAKER || aic3008_tx_mode == CALL_UPLINK_IMIC_SPEAKER_DUALMIC || aic3008_tx_mode == VOIP_UPLINK_IMIC_SPEAKER || aic3008_tx_mode == CALL_UPLINK_IMIC_SPEAKER_DUALMIC_WB ) { if (!aic3008_power_ctl->isPowerOn) { AUD_INFO("[PWR] codec was power off wakeup first\n"); aic3008_powerup(); aic3008_config(DISABLE_AUD_HPMIC_EXT_ONLY, ARRAY_SIZE(DISABLE_AUD_HPMIC_EXT_ONLY)); aic3008_powerdown(); } else aic3008_config(DISABLE_AUD_HPMIC_EXT_ONLY, ARRAY_SIZE(DISABLE_AUD_HPMIC_EXT_ONLY)); } else { if (!aic3008_power_ctl->isPowerOn) { AUD_INFO("[PWR] codec was power off wakeup first\n"); aic3008_powerup(); aic3008_config(DISABLE_AUD_HPMIC_EXT, ARRAY_SIZE(DISABLE_AUD_HPMIC_EXT)); aic3008_powerdown(); } else aic3008_config(DISABLE_AUD_HPMIC_EXT, ARRAY_SIZE(DISABLE_AUD_HPMIC_EXT)); } } }
int route_rx_enable(int path, int en) { AUD_DBG("[RX] (%d, %d) uses AIC3008 default RX setting...\n", path, en); if (en) { /* Downlink_Wakeup */ AUD_INFO("[RX] route_rx_enable call Downlink_Wakeup"); aic3008_config(CODEC_DOWNLINK_ON, ARRAY_SIZE(CODEC_DOWNLINK_ON)); /* Path switching */ switch (path) { default: /* By pass */ AUD_INFO("[RX] route_rx_enable call DOWNLINK_IMIC_RECEIVER"); aic3008_config(DOWNLINK_IMIC_RECEIVER, ARRAY_SIZE(DOWNLINK_IMIC_RECEIVER)); break; } } else { /* Downlink_Off */ AUD_INFO("[RX] route_rx_enable call CODEC_DOWNLINK_OFF"); aic3008_config(CODEC_DOWNLINK_OFF, ARRAY_SIZE(CODEC_DOWNLINK_OFF)); } return 0; }
/* write a register then read a register, compare them ! */ static int32_t spi_write_read_list(CODEC_SPI_CMD *cmds, int num) { int i; int rc; unsigned char write_buffer[2]; unsigned char read_result[2] = { 0, 0 }; if (!codec_spi_dev) return 0; codec_spi_dev->bits_per_word = 16; for (i = 0; i < num; i++) { /*if writing page, then don't read its value */ switch (cmds[i].act) { case 'w': write_buffer[1] = cmds[i].reg << 1; write_buffer[0] = cmds[i].data; if (cmds[i].reg == 0x00 || cmds[i].reg == 0x7F) { rc = spi_write(codec_spi_dev, write_buffer, sizeof(write_buffer)); if (rc < 0) return rc; if(cmds[i].reg == 0x00) { AUD_DBG("------ write page: 0x%02X ------\n", cmds[i].data); } else if(cmds[i].reg == 0x7F) { AUD_DBG("------ write book: 0x%02X ------\n", cmds[i].data); } } else { rc = spi_write_then_read(codec_spi_dev, write_buffer, 2, read_result, 2); if (rc < 0) return rc; if (read_result[0] != cmds[i].data) AUD_INFO("incorrect value,reg 0x%02x, write 0x%02x, read 0x%02x", cmds[i].reg, cmds[i].data, read_result[0]); } break; case 'd': msleep(cmds[i].data); break; default: break; } } return 0; }
static int spi_aic3008_probe(struct spi_device *spi_aic3008) { AUD_DBG("spi device: %s, addr = 0x%p. YAY! ***** Start to Test *****\n", spi_aic3008->modalias, spi_aic3008); int ret = 0; codec_spi_dev = spi_aic3008; /* assign global pointer to SPI device. */ struct aic3008_priv *aic3008 = kzalloc(sizeof(struct aic3008_priv), GFP_KERNEL);; if (aic3008 == NULL) return -ENOMEM; spi_set_drvdata(spi_aic3008, aic3008); ret = snd_soc_register_codec(&spi_aic3008->dev, &soc_codec_dev_aic3008, &aic3008_dai, 1); if (ret < 0) { AUD_ERR("snd_soc_register_codec() Failed\n"); kfree(aic3008); } AUD_INFO("SPI control for AIC3008 started successfully...\n"); return ret; }
static void aic3008_powerdown(void) { int64_t t1, t2; t1 = ktime_to_ms(ktime_get()); if (aic3008_uplink != NULL) { AUD_DBG("[PWR] power off AIC3008 by table len(%d)\n", (aic3008_uplink[POWER_OFF][0].data-1)); aic3008_config(&aic3008_uplink[POWER_OFF][1], aic3008_uplink[POWER_OFF][0].data); } else { AUD_DBG("[PWR] power off AIC3008 by default len(%d)\n", (ARRAY_SIZE(CODEC_POWER_OFF))); aic3008_config(CODEC_POWER_OFF, ARRAY_SIZE(CODEC_POWER_OFF)); } aic3008_power_ctl->suspend(); t2 = ktime_to_ms(ktime_get()) - t1; AUD_INFO("[PWR] power down AIC3008 %lldms\n", t2); return; }
static long aic3008_ioctl(struct file *file, unsigned int cmd, unsigned long argc) { struct AIC3008_PARAM para; void *table; int ret = 0, i = 0, mem_size, volume = 0; CODEC_SPI_CMD reg[4]; unsigned char data; int len= 0; /* for dump dsp length. */ int pcbid = 0; AUD_DBG("IOCTL command:0x%02X, argc:%ld\n", cmd, argc); if (aic3008_uplink == NULL || aic3008_downlink == NULL || aic3008_minidsp == NULL) { AUD_ERR("cmd 0x%x, invalid pointers\n", cmd); return -EFAULT; } switch (cmd) { /* first IO command from HAL */ case AIC3008_IO_SET_TX_PARAM: /* second IO command from HAL */ case AIC3008_IO_SET_RX_PARAM: if (copy_from_user(¶, (void *) argc, sizeof(para))) { AUD_ERR("failed on copy_from_user\n"); return -EFAULT; } AUD_DBG("parameters(%d, %d, %p)\n", para.row_num, para.col_num, para.cmd_data); if (cmd == AIC3008_IO_SET_TX_PARAM) table = aic3008_uplink[0]; else table = aic3008_downlink[0]; /* confirm indicated size doesn't exceed the allocated one */ if (para.row_num > IO_CTL_ROW_MAX || para.col_num != IO_CTL_COL_MAX) { AUD_ERR("data size mismatch with allocated memory (%d,%d)\n", IO_CTL_ROW_MAX, IO_CTL_COL_MAX); return -EFAULT; } mem_size = para.row_num * para.col_num * sizeof(CODEC_SPI_CMD); if (copy_from_user(table, para.cmd_data, mem_size)) { AUD_ERR("failed on copy_from_user\n"); return -EFAULT; } /* invoking initialization procedure of AIC3008 */ if (cmd == AIC3008_IO_SET_TX_PARAM) aic3008_tx_config(INITIAL); AUD_INFO("update RX/TX tables(%d, %d) successfully\n", para.row_num, para.col_num); break; /* third io command from HAL */ case AIC3008_IO_SET_DSP_PARAM: if (copy_from_user(¶, (void *) argc, sizeof(para))) { AUD_ERR("failed on copy_from_user\n"); return -EFAULT; } AUD_DBG("parameters(%d, %d, %p)\n", para.row_num, para.col_num, para.cmd_data); table = aic3008_minidsp[0]; /* confirm indicated size doesn't exceed the allocated one */ if (para.row_num > MINIDSP_ROW_MAX || para.col_num != MINIDSP_COL_MAX) { AUD_ERR("data size mismatch with allocated memory (%d, %d)\n", MINIDSP_ROW_MAX, MINIDSP_COL_MAX); return -EFAULT; } mem_size = para.row_num * para.col_num * sizeof(CODEC_SPI_CMD); if (copy_from_user(table, para.cmd_data, mem_size)) { AUD_ERR("failed on copy_from_user\n"); return -EFAULT; } AUD_INFO("update dsp table(%d, %d) successfully\n", para.row_num, para.col_num); break; /* these IO commands are called to set path */ case AIC3008_IO_CONFIG_TX: case AIC3008_IO_CONFIG_RX: case AIC3008_IO_CONFIG_MEDIA: if (copy_from_user(&i, (void *) argc, sizeof(int))) { AUD_ERR("failed on copy_from_user\n"); return -EFAULT; } /* call aic3008_set_config() to issue SPI commands */ ret = aic3008_set_config(cmd, i, 1); if (ret < 0) AUD_ERR("configure(%d) error %d\n", i, ret); break; case AIC3008_IO_CONFIG_VOLUME_L: if (copy_from_user(&volume, (void *) argc, sizeof(int))) { AUD_ERR("failed on copy_from_user\n"); return -EFAULT; } if (volume < -127 || volume > 48) { AUD_ERR("volume out of range\n"); return -EFAULT; } AUD_DBG("AIC3008 config left volume %d\n", volume); CODEC_SET_VOLUME_L[1].data = volume; /* call extended aic3008_config_ex() to set up volume */ aic3008_config_ex(CODEC_SET_VOLUME_L, ARRAY_SIZE(CODEC_SET_VOLUME_L)); break; case AIC3008_IO_CONFIG_VOLUME_R: if (copy_from_user(&volume, (void *) argc, sizeof(int))) { AUD_ERR("failed on copy_from_user\n"); return -EFAULT; } if (volume < -127 || volume > 48) { AUD_ERR("volume out of range\n"); return -EFAULT; } AUD_DBG("AIC3008 config right volume %d\n", volume); CODEC_SET_VOLUME_R[1].data = volume; /* call extended aic3008_config_ex() to set up volume */ aic3008_config_ex(CODEC_SET_VOLUME_R, ARRAY_SIZE(CODEC_SET_VOLUME_R)); break; /* dump specific audio codec page */ case AIC3008_IO_DUMP_PAGES: if (copy_from_user(&i, (void *) argc, sizeof(int))) { AUD_ERR("failed on copy_from_user\n"); return -EFAULT; } if (i > AIC3008_MAX_PAGES) { AUD_ERR("invalid page number %d\n", i); return -EINVAL; } AUD_DBG("========== dump page %d ==========\n", i); /* indicated page number of AIC3008 */ codec_spi_write(0x00, i, true); for (i = 0; i < AIC3008_MAX_REGS; i++) { ret = codec_spi_read(i, &data, true); if (ret < 0) { AUD_ERR("read fail on register 0x%X\n", i); 0; } else { AUD_DBG("(addr:0x%02X, data:0x%02X)\n", i, data); 0; } } AUD_DBG("=============================================\n"); break; case AIC3008_IO_DUMP_DSP: AUD_DBG("========== dump dsp %d ==========\n", aic3008_dsp_mode); /* indicated dsp number of AIC3008 */ len = (aic3008_minidsp[aic3008_dsp_mode][0].reg << 8) | aic3008_minidsp[aic3008_dsp_mode][0].data; AUD_DBG("len = %d", len); spi_read_list(&aic3008_minidsp[aic3008_dsp_mode][1], len); AUD_DBG("=============================================\n"); break; /* write specific audio codec register */ case AIC3008_IO_WRITE_REG: AUD_INFO("========== WRITE_REG ==========\n"); if (copy_from_user(®, (void *) argc, sizeof(CODEC_SPI_CMD) * 4)) { AUD_ERR("failed on copy_from_user\n"); return -EFAULT; } AUD_DBG("command list (%c,%02X,%02X) (%c,%02X,%02X) (%c,%02X,%02X) (%c,%02X,%02X)\n", reg[0].act, reg[0].reg, reg[0].data, reg[1].act, reg[1].reg, reg[1].data, reg[2].act, reg[2].reg, reg[2].data, reg[3].act, reg[3].reg, reg[3].data); // aic3008_config_ex(reg, 4); for (i = 0; i < 4; i++) { if (reg[i].act == 'r' || reg[i].act == 'R') codec_spi_read(reg[i].reg, ®[i].data, true); else if (reg[i].act == 'w' || reg[i].act == 'W') codec_spi_write(reg[i].reg, reg[i].data, true); else return -EINVAL; } AUD_INFO("========== WRITE_REG end ==========\n"); break; /* read specific audio codec register */ case AIC3008_IO_READ_REG: AUD_INFO("========== READ_REG ==========\n"); if (copy_from_user(®, (void *) argc, sizeof(CODEC_SPI_CMD) * 4)) { AUD_ERR("failed on copy_from_user\n"); return -EFAULT; } for (i = 0; i < 4; i++) { if (reg[i].act == 'r' || reg[i].act == 'R') codec_spi_read(reg[i].reg, ®[i].data, true); else if (reg[i].act == 'w' || reg[i].act == 'W') codec_spi_write(reg[i].reg, reg[i].data, true); else return -EINVAL; } if (copy_to_user((void *) argc, ®, sizeof(CODEC_SPI_CMD) * 2)) { AUD_ERR("failed on copy_to_user\n"); return -EFAULT; } AUD_INFO("========== READ_REG end==========\n"); break; case AIC3008_IO_POWERDOWN: /* power down IO command */ mutex_lock(&lock); aic3008_powerdown(); mutex_unlock(&lock); break; case AIC3008_IO_LOOPBACK: /* loopback IO command */ if (copy_from_user(&i, (void *) argc, sizeof(int))) { AUD_ERR("failed on copy_from_user\n"); return -EFAULT; } AUD_DBG("index %d for LOOPBACK\n", i); /* set up the loopback with specific id */ aic3008_set_loopback(i); break; case AIC3008_IO_GET_PCBID: /* get pcbid */ pcbid = htc_get_pcbid_info(); if (copy_to_user((void *) argc, &pcbid, sizeof(int))) { AUD_ERR("failed on copy_to_user\n"); return -EFAULT; } break; default: AUD_ERR("invalid command %d\n", _IOC_NR(cmd)); ret = -EINVAL; } return ret; }
static int aic3008_set_config(int config_tbl, int idx, int en) { int rc = 0, len = 0; int64_t t1, t2; mutex_lock(&lock); /* spi_aic3008_prevent_sleep(); */ switch (config_tbl) { case AIC3008_IO_CONFIG_TX: /* TX */ aic3008_AmpSwitch(aic3008_tx_mode, 0); if(!aic3008_power_ctl->isPowerOn) { AUD_ERR("[TX] AIC3008 is power off now, can't do IO CONFIG TX = %d, please check this condition!!", idx); AUD_ERR("[TX] Since IO CONFIG TX = %d can't be done, it maybe no sound on device"); break; } if (en) { AUD_INFO("[TX] AIC3008_IO_CONFIG_TX: UPLINK idx = %d",idx); aic3008_tx_config(idx); aic3008_tx_mode = idx; aic3008_AmpSwitch(idx, 1); } else { AUD_INFO("[TX] AIC3008_IO_CONFIG_TX: UPLINK_PATH_OFF"); aic3008_tx_config(UPLINK_PATH_OFF); aic3008_tx_mode = UPLINK_PATH_OFF; } if ((aic3008_tx_mode == UPLINK_PATH_OFF) && (aic3008_rx_mode == DOWNLINK_PATH_OFF)) { AUD_INFO("[TX] AIC3008_IO_CONFIG_TX: PATH OFF Call aic3008_powerdown()"); aic3008_powerdown(); } break; case AIC3008_IO_CONFIG_RX: /* RX */ aic3008_AmpSwitch(aic3008_rx_mode, 0); if(!aic3008_power_ctl->isPowerOn) { AUD_ERR("[RX] AIC3008 is power off now, can't do IO CONFIG RX = %d, please check this condition!!", idx); AUD_ERR("[RX] Since IO CONFIG RX = %d can't be done, it maybe no sound on device"); break; } if(!first_boot_path && idx == 10) { AUD_INFO("[RX] AIC3008_IO_CONFIG_RX: first_boot_path = 10\n"); idx = DOWNLINK_PATH_OFF; aic3008_rx_mode = DOWNLINK_PATH_OFF; first_boot_path = true; } if (en) { AUD_INFO("[RX] AIC3008_IO_CONFIG_RX: DOWNLINK idx = %d",idx); aic3008_rx_config(idx); aic3008_rx_mode = idx; aic3008_AmpSwitch(idx, 1); } else { AUD_INFO("[RX] AIC3008_IO_CONFIG_RX: DOWNLINK_PATH_OFF"); aic3008_rx_config(DOWNLINK_PATH_OFF); aic3008_rx_mode = DOWNLINK_PATH_OFF; } if ((aic3008_tx_mode == UPLINK_PATH_OFF) && (aic3008_rx_mode == DOWNLINK_PATH_OFF)) { AUD_INFO("[RX] AIC3008_IO_CONFIG_RX: PATH OFF Call aic3008_powerdown()"); aic3008_powerdown(); } break; case AIC3008_IO_CONFIG_MEDIA: if(idx == 20 && !aic3008_power_ctl->isPowerOn) { aic3008_powerup(); break; } else if(idx == 49) { AUD_DBG("[DSP] idx = %d, Mic Mute!!", idx); if (aic3008_tx_mode == VOIP_UPLINK_BT || aic3008_tx_mode == UPLINK_BT_AP || aic3008_tx_mode == UPLINK_BT_BB ){ aic3008_config(BT_MIC_MUTE, ARRAY_SIZE(BT_MIC_MUTE)); // mute mic } else{ aic3008_config(ADC_MUTE, ARRAY_SIZE(ADC_MUTE)); // mute mic } break; } else if(idx == 50) { AUD_DBG("[DSP] idx = %d, Mic unMute!!", idx); if (aic3008_tx_mode == VOIP_UPLINK_BT || aic3008_tx_mode == UPLINK_BT_AP || aic3008_tx_mode == UPLINK_BT_BB ){ aic3008_config(BT_MIC_UNMUTE, ARRAY_SIZE(BT_MIC_UNMUTE)); // mute mic } else{ aic3008_config(ADC_UNMUTE, ARRAY_SIZE(ADC_UNMUTE)); // mute mic } break; } else if(idx == 51) { AUD_DBG("[DSP] idx = %d, Output Mute!!", idx); aic3008_config(DAC_MUTE, ARRAY_SIZE(DAC_MUTE)); // mute output break; } else if(idx == 52) { AUD_DBG("[DSP] idx = %d, Output unMute!!", idx); aic3008_config(DAC_UNMUTE, ARRAY_SIZE(DAC_UNMUTE)); // unmute output break; } else if(idx == 53) { AUD_INFO("[DSP] idx = %d, BEATS_ON!!", idx); aic3008_config(BEATS_ON, ARRAY_SIZE(BEATS_ON)); // Increase the gain for BEATS_EFFECT_ON break; } else if(idx == 54) { AUD_INFO("[DSP] idx = %d, BEATS_OFF!!", idx); aic3008_config(BEATS_OFF, ARRAY_SIZE(BEATS_OFF)); // Decrease the gain for BEATS_EFFECT_OFF break; } if(!aic3008_power_ctl->isPowerOn) { // AUD_ERR("[DSP] AIC3008 is power off now, do you want change DSP = %d??", idx); // AUD_ERR("[DSP] If DSP %d must to be done, please make sure I/O config won't be forgot!!", idx); aic3008_powerup(); AUD_INFO("[DSP] Recovery this condition, AIC3008 is power up now and DSP %d will be config", idx); } if (aic3008_minidsp == NULL) { AUD_INFO("[DSP] AIC3008_IO_CONFIG_MEDIA: aic3008_minidsp == NULL"); rc = -EFAULT; break; } /* we use this value to dump dsp. */ aic3008_dsp_mode = idx; len = (aic3008_minidsp[idx][0].reg << 8) | aic3008_minidsp[idx][0].data; AUD_INFO("[DSP] AIC3008_IO_CONFIG_MEDIA: Original RX %d, TX %d. start DSP = %d, len = %d ++. ", aic3008_rx_mode, aic3008_tx_mode, idx, len); t1 = ktime_to_ms(ktime_get()); /* step 1: path off first */ if (aic3008_rx_mode != DOWNLINK_PATH_OFF) aic3008_rx_config(DOWNLINK_PATH_OFF); /* step 2: config DSP */ aic3008_config(&aic3008_minidsp[idx][1], len); t2 = ktime_to_ms(ktime_get()) - t1; AUD_INFO("[DSP] AIC3008_IO_CONFIG_MEDIA: configure miniDSP index(%d) time: %lldms --\n", idx, (t2)); break; } /* spi_aic3008_allow_sleep(); */ mutex_unlock(&lock); return rc; }