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; }
/* 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 int aic3008_config_ex(CODEC_SPI_CMD *cmds, int size) { int i = 0; int ret = -EINVAL; struct spi_transfer *spi_t_cmds = NULL; struct spi_message m; unsigned char *buffer = NULL; unsigned char *ptr = NULL; if (!codec_spi_dev) { AUD_ERR("no spi device\n"); return -EFAULT; } if (cmds == NULL || size == 0) { AUD_ERR("invalid spi parameters\n"); return -EINVAL; } spi_t_cmds = kmalloc(size * sizeof(struct spi_transfer), GFP_KERNEL); if (spi_t_cmds == NULL) { AUD_ERR("kmalloc spi transfer struct fail\n"); goto error; } else memset(spi_t_cmds, 0, size * sizeof(struct spi_transfer)); buffer = kmalloc(size * 2 * sizeof(unsigned char), GFP_KERNEL); if (buffer == NULL) { AUD_ERR("kmalloc buffer fail\n"); goto error; } else memset(buffer, 0, size * sizeof(CODEC_SPI_CMD) * sizeof(unsigned char)); spi_message_init(&m); for (i = 0, ptr = buffer; i < size; i++, ptr += 2) { ptr[0] = cmds[i].reg << 1; ptr[1] = cmds[i].data; spi_t_cmds[i].tx_buf = ptr; spi_t_cmds[i].len = 2; spi_message_add_tail(&spi_t_cmds[i], &m); } codec_spi_dev->bits_per_word = 16; ret = spi_sync(codec_spi_dev, &m); error: if (buffer != NULL) kfree(buffer); if (spi_t_cmds != NULL) kfree(spi_t_cmds); return ret; }
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; }
static void aic3008_powerinit(void) { int value = htc_get_pcbid_info(); if (value >= PROJECT_PHASE_XA) { power_config("AUD_MCLK_EN", TEGRA_GPIO_PX7, INIT_OUTPUT_HIGH); power_config("AUD_MCLK_EN", TEGRA_GPIO_PX7, GPIO_OUTPUT); power_config("AUD_AIC3008_RST#", TEGRA_GPIO_PW5, INIT_OUTPUT_HIGH); power_config("AUD_AIC3008_RST#", TEGRA_GPIO_PW5, GPIO_OUTPUT); power_config("v_aud_a1v8", TEGRA_GPIO_PD2, REGULATOR_METHOD); power_config("v_aud_3v3", TEGRA_GPIO_PB2, REGULATOR_METHOD); } else AUD_ERR("%s: no pcbid satisfy.", __func__); power_config("AUD_MCLK", TEGRA_GPIO_PW4, INIT_OUTPUT_LOW); sfio_deconfig("AUD_MCLK", TEGRA_GPIO_PW4); power_config("AUD_SPK_EN", TEGRA_GPIO_PP6, INIT_OUTPUT_LOW); sfio_deconfig("AUD_SPK_EN", TEGRA_GPIO_PP6); power_config("AUD_LINEOUT_EN", TEGRA_GPIO_PP7, INIT_OUTPUT_LOW); sfio_deconfig("AUD_LINEOUT_EN", TEGRA_GPIO_PP7); common_init(); spin_lock_init(&aic3008_power.spin_lock); aic3008_power.isPowerOn = true; return; }
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; }
static void aic3008_amp_powerdown(int type) { switch (type) { case HEADSET_AMP: if (pcbid >= PROJECT_PHASE_XB || board_get_sku_tag() == 0x34600) { power_deconfig("AUD_HEADPHONE_EN", TEGRA_GPIO_PP7, GPIO_OUTPUT); } break; case SPEAKER_AMP: if (pcbid >= PROJECT_PHASE_XB || board_get_sku_tag() == 0x34600) { #if (defined(CONFIG_SND_AMP_TFA9887)) set_tfa9887_spkamp(0, 0); #endif } else { power_deconfig("AUD_SPK_EN", TEGRA_GPIO_PP6, GPIO_OUTPUT); } break; case DOCK_AMP: if (pcbid >= PROJECT_PHASE_XB || board_get_sku_tag() == 0x34600) { } else { power_deconfig("AUD_LINEOUT_EN", TEGRA_GPIO_PP7, GPIO_OUTPUT); } dock_config("TEGRA_GPIO_DESK_AUD", TEGRA_GPIO_PCC5, false, true); break; default: AUD_ERR("aic3008_amp_powerdown unknown type %d\n", type); break; } return; }
static void aic3008_suspend(void) { spin_lock(&aic3008_power_ctl->spin_lock); if (pcbid >= PROJECT_PHASE_XD) { power_deconfig("AUD_MCLK_EN", TEGRA_GPIO_PN1, GPIO_OUTPUT); } else if (pcbid >= PROJECT_PHASE_XA && pcbid <= PROJECT_PHASE_XC) { power_deconfig("AUD_MCLK_EN", TEGRA_GPIO_PX7, GPIO_OUTPUT); } else AUD_ERR("%s: no pcbid satisfy.", __func__); common_deconfig(); aic3008_power_ctl->isPowerOn = false; spin_unlock(&aic3008_power_ctl->spin_lock); 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_DBG("[PWR] ***** regulator %s %d disable *****\n", name, pin); break; case GPIO_OUTPUT: gpio_set_value(pin, 0); AUD_DBG("[PWR] ***** gpio %s %d disable *****\n", name, pin); break; default: AUD_ERR("[PWR] ***** power_deconfig nothing *****\n"); } return; }
static inline int spi_aic3008_init(void) { int ret = 0; AUD_DBG("starting aic3008 spi driver init...\n"); mutex_init(&lock); ret = spi_register_driver(&aic3008_spi_driver); AUD_DBG("DONE spi_register_driver(&aic3008_spi_driver).\n"); if (ret < 0) { AUD_ERR("failed to register spi driver(%d)\n", ret); return ret; } ret = misc_register(&aic3008_misc); AUD_DBG("DONE misc_register(&aic3008_misc).\n"); if (ret < 0) { AUD_ERR("failed to register misc device(%d)\n", ret); spi_unregister_driver(&aic3008_spi_driver); return ret; } return 0; }
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_DBG("[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_DBG("[PWR] ***** gpio %s %d disable *****\n", name, pin); } return; }
static int aic3008_open(struct inode *inode, struct file *pfile) { int ret = 0; AUD_DBG("IOCTL open\n"); mutex_lock(&lock); if (aic3008_opened) { AUD_ERR("busy\n"); ret = -EBUSY; } else aic3008_opened = 1; mutex_unlock(&lock); return ret; }
static int tpa6185_i2c_write(char *txData, int length) { if (this_client == NULL) { AUD_ERR("tpa6185_i2c_write client is NULL\n"); return -EFAULT; } int rc; struct i2c_msg msgs[] = { { .addr = this_client->addr, .flags = 0, .len = length, .buf = txData, }, };
static ssize_t codec_debug_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { char *access_str = filp->private_data; char lbuf[32]; unsigned char reg_idx[2] = {0x00, 0x00}; int rc; long int param[5]; if (cnt > sizeof(lbuf) - 1) return -EINVAL; rc = copy_from_user(lbuf, ubuf, cnt); if (rc) return -EFAULT; lbuf[cnt] = '\0'; if (!strcmp(access_str, "poke")) { /* write */ rc = get_parameters(lbuf, param, 2); if ((param[0] <= 0xFF) && (param[1] <= 0xFF) && (rc == 0)) { reg_idx[0] = param[0]; reg_idx[1] = param[1]; tfa9887_i2c_write(reg_idx, 2); } else rc = -EINVAL; } else if (!strcmp(access_str, "peek")) { /* read */ rc = get_parameters(lbuf, param, 1); if ((param[0] <= 0xFF) && (rc == 0)) { reg_idx[0] = param[0]; tfa9887_i2c_read(&read_data, 1); } else rc = -EINVAL; } if (rc == 0) rc = cnt; else AUD_ERR("%s: rc = %d\n", __func__, rc); return rc; }
static void aic3008_amp_powerdown(int type) { switch (type) { case HEADSET_AMP: break; case SPEAKER_AMP: power_deconfig("AUD_SPK_EN", TEGRA_GPIO_PP6, GPIO_OUTPUT); break; case DOCK_AMP: power_deconfig("AUD_LINEOUT_EN", TEGRA_GPIO_PP7, GPIO_OUTPUT); dock_config("TEGRA_GPIO_DESK_AUD", TEGRA_GPIO_PCC5, false, true); break; default: AUD_ERR("aic3008_amp_powerdown unknown type %d\n", type); break; } return; }
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_powerinit(void) { if (pcbid >= PROJECT_PHASE_XD) { power_config("AUD_MCLK_EN", TEGRA_GPIO_PN1, INIT_OUTPUT_HIGH); } else if (pcbid >= PROJECT_PHASE_XA && pcbid <= PROJECT_PHASE_XC) { power_config("AUD_MCLK_EN", TEGRA_GPIO_PX7, INIT_OUTPUT_HIGH); } else AUD_ERR("%s: no pcbid satisfy.", __func__); power_config("AUD_AIC3008_RST#", TEGRA_GPIO_PW5, INIT_OUTPUT_HIGH); power_config("v_aud_a1v8", TEGRA_GPIO_PD2, REGULATOR_METHOD); power_config("v_aud_3v3", TEGRA_GPIO_PB2, REGULATOR_METHOD); power_config("AUD_MCLK", TEGRA_GPIO_PW4, INIT_OUTPUT_LOW); sfio_deconfig("AUD_MCLK", TEGRA_GPIO_PW4); power_config("AUD_SPK_EN", TEGRA_GPIO_PP6, INIT_OUTPUT_LOW); sfio_deconfig("AUD_SPK_EN", TEGRA_GPIO_PP6); power_config("AUD_LINEOUT_EN", TEGRA_GPIO_PP7, INIT_OUTPUT_LOW); sfio_deconfig("AUD_LINEOUT_EN", TEGRA_GPIO_PP7); common_init(); spin_lock_init(&aic3008_power_ctl->spin_lock); aic3008_power_ctl->isPowerOn = true; 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; }
static int tegra_hifi_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_codec *codec = rtd->codec; struct snd_soc_card *card = codec->card; struct tegra_aic3008 *machine = snd_soc_card_get_drvdata(card); int dai_flag = 0, mclk, srate; int err; AUD_DBG("Start tegra_hifi_hw_params()\n"); AUD_DBG("set I2S Master\n"); dai_flag |= SND_SOC_DAIFMT_I2S; // i2s mode dai_flag |= SND_SOC_DAIFMT_CBM_CFM; // bclk and frame master srate = params_rate(params); switch (srate) { case 64000: case 88200: case 96000: mclk = 128 * srate; break; default: mclk = 256 * srate; break; } /* FIXME: Codec only requires >= 3MHz if OSR==0 */ while (mclk < 6000000) mclk *= 2; mclk = 12288000; err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); if (err < 0) { dev_err(card->dev, "Can't configure clocks\n"); return err; } // eventually calls audio codec to set dai format, which sets slave err = snd_soc_dai_set_fmt(codec_dai, dai_flag); if (err < 0) { AUD_ERR("codec_dai fmt not set \n"); return err; } AUD_DBG("*** snd_soc_dai_set_fmt(codec_dai, dai_flag) ok ***\n"); // eventually calls t2s driver to set dai format, which sets master err = snd_soc_dai_set_fmt(cpu_dai, dai_flag); if (err < 0) { AUD_ERR("cpu_dai fmt not set \n"); return err; } AUD_DBG("*** snd_soc_dai_set_fmt(cpu_dai, dai_flag) ok ***\n"); // FIXME: not sure this is the right way. // Sets the audio codec clks. // This should be sample rate times 256 or 128 based on codec need err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, SND_SOC_CLOCK_IN); if (err < 0) { AUD_ERR("codec_dai clock not set\n"); return err; } AUD_DBG("*** snd_soc_dai_set_sysclk(codec_dai, 0, mclk, SND_SOC_CLOCK_IN) ok ***\n"); return 0; }