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;
}
Example #7
0
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;
}
Example #8
0
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;
}
Example #13
0
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,
		},
	};
Example #14
0
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;
}
Example #15
0
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;
}
Example #17
0
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(&para, (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(&para, (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(&reg, (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, &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(&reg, (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, &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, &reg, 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;
}
Example #20
0
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;
}