/* 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 int32_t spi_write_table_parsepage(CODEC_SPI_CMD *cmds, int num)
{
	int i;
	int bulk_counter;
	int status = 0;
	struct spi_message	m;
	struct spi_transfer	tx_addr;
	unsigned char page_select = (unsigned char)0;
	unsigned int reg_long1, reg_long2;

	if (codec_dev == NULL) {
		status = -ESHUTDOWN;
		return status;
	}

	i = 0;

	while (i < num) {
		if (cmds[i].reg == page_select) {
			/* select page */
			codec_spi_write(cmds[i].reg, cmds[i].data);
			i++;
		} else {
			spi_message_init(&m);
			memset(bulk_tx, 0, MINIDSP_COL_MAX * 2 * \
				sizeof(uint8_t));
			memset(&tx_addr, 0, sizeof(struct spi_transfer));

			bulk_counter = 0;
			bulk_tx[bulk_counter] = cmds[i].reg << 1;
			bulk_tx[bulk_counter + 1] = cmds[i].data;
			bulk_counter += 2;

			do {
				reg_long1 = (unsigned int)cmds[i].reg;
				reg_long2 = (unsigned int)cmds[i+1].reg;
				if (reg_long2 == (reg_long1+1)) {
					bulk_tx[bulk_counter] = cmds[i+1].data;
					bulk_counter++;
				}
				i++;
			} while (reg_long2 == (reg_long1+1));

			tx_addr.tx_buf = bulk_tx;
			tx_addr.len = (bulk_counter);
			tx_addr.cs_change = 1;
			tx_addr.bits_per_word = 8;
			spi_message_add_tail(&tx_addr, &m);
			status = spi_sync(codec_dev, &m);
		}
	}

	return status;
}
Ejemplo n.º 3
0
static long aic3254_ioctl(struct file *file, unsigned int cmd,
	   unsigned long argc)
{
	struct AIC3254_PARAM para;
	void *table;
	int ret = 0, i = 0, mem_size, volume = 0;
	CODEC_SPI_CMD reg[2];
	unsigned char data;

	if (aic3254_uplink == NULL ||
		aic3254_downlink == NULL ||
		aic3254_minidsp == NULL) {
		pr_aud_err("%s: cmd 0x%x, invalid pointers\n", __func__, cmd);
		return -EFAULT;
	}

	switch (cmd) {
	case AIC3254_SET_TX_PARAM:
	case AIC3254_SET_RX_PARAM:
		if (copy_from_user(&para, (void *)argc, sizeof(para))) {
			pr_aud_err("%s: failed on copy_from_user\n", __func__);
			return -EFAULT;
		}

		pr_aud_info("%s: parameters(%d, %d, %p)\n", __func__,
				para.row_num, para.col_num, para.cmd_data);
		if (cmd == AIC3254_SET_TX_PARAM)
			table = aic3254_uplink[0];
		else
			table = aic3254_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) {
			pr_aud_err("%s: data size mismatch with allocated"
					" memory (%d,%d)\n", __func__,
					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)) {
			pr_aud_err("%s: failed on copy_from_user\n", __func__);
			return -EFAULT;
		}

		/* invoking initialization procedure of AIC3254 */
		if (cmd == AIC3254_SET_TX_PARAM)
			aic3254_tx_config(INITIAL);

		pr_aud_info("%s: update table(%d,%d) successfully\n",
				__func__, para.row_num, para.col_num);
			break;
	case AIC3254_SET_DSP_PARAM:
		if (copy_from_user(&para, (void *)argc, sizeof(para))) {
			pr_aud_err("%s: failed on copy_from_user\n", __func__);
			return -EFAULT;
		}

		pr_aud_info("%s: parameters(%d, %d, %p)\n", __func__,
				para.row_num, para.col_num, para.cmd_data);

		table = aic3254_minidsp[0];

		/* confirm indicated size doesn't exceed the allocated one */
		if (para.row_num > MINIDSP_ROW_MAX
				|| para.col_num != MINIDSP_COL_MAX) {
			pr_aud_err("%s: data size mismatch with allocated"
					" memory (%d,%d)\n", __func__,
					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)) {
			pr_aud_err("%s: failed on copy_from_user\n", __func__);
			return -EFAULT;
		}

		pr_aud_info("%s: update table(%d,%d) successfully\n",
				__func__, para.row_num, para.col_num);
		break;
	case AIC3254_CONFIG_TX:
	case AIC3254_CONFIG_RX:
	case AIC3254_CONFIG_MEDIA:
		if (copy_from_user(&i, (void *)argc, sizeof(int))) {
			pr_aud_err("%s: failed on copy_from_user\n", __func__);
			return -EFAULT;
		}
		ret = aic3254_set_config(cmd, i, 1);
		if (ret < 0)
			pr_aud_err("%s: configure(%d) error %d\n",
				__func__, i, ret);
		break;
	case AIC3254_CONFIG_VOLUME_L:
		if (copy_from_user(&volume, (void *)argc, sizeof(int))) {
			pr_aud_err("%s: failed on copy_from_user\n", __func__);
			return -EFAULT;
		}

		if (volume < -127 || volume > 48) {
			pr_aud_err("%s: volume out of range\n", __func__);
			return -EFAULT;
		}

		pr_aud_info("%s: AIC3254 config left volume %d\n",
				__func__, volume);

		CODEC_SET_VOLUME_L[1].data = volume;
		aic3254_config_ex(CODEC_SET_VOLUME_L, ARRAY_SIZE(CODEC_SET_VOLUME_L));
		break;
	case AIC3254_CONFIG_VOLUME_R:
		if (copy_from_user(&volume, (void *)argc, sizeof(int))) {
			pr_aud_err("%s: failed on copy_from_user\n", __func__);
			return -EFAULT;
		}

		if (volume < -127 || volume > 48) {
			pr_aud_err("%s: volume out of range\n", __func__);
			return -EFAULT;
		}

		pr_aud_info("%s: AIC3254 config right volume %d\n",
				__func__, volume);

		CODEC_SET_VOLUME_R[1].data = volume;
		aic3254_config_ex(CODEC_SET_VOLUME_R, ARRAY_SIZE(CODEC_SET_VOLUME_R));
		break;
	case AIC3254_DUMP_PAGES:
		if (copy_from_user(&i, (void *)argc, sizeof(int))) {
			pr_aud_err("%s: failed on copy_from_user\n", __func__);
			return -EFAULT;
		}
		if (i > AIC3254_MAX_PAGES) {
			pr_aud_err("%s: invalid page number %d\n", __func__, i);
			return -EINVAL;
		}

		pr_aud_info("========== %s: dump page %d ==========\n",
				__func__, i);
		/* indicated page number to AIC3254 */
		if (ctl_ops->rx_amp_enable)
			ctl_ops->rx_amp_enable(1);
		codec_spi_write(0x00, i);
		for (i = 0; i < AIC3254_MAX_REGS; i++) {
			ret = codec_spi_read(i, &data);
			if (ret < 0)
				pr_aud_err("read fail on register 0x%X\n", i);
			else
				pr_aud_info("(0x%02X, 0x%02X)\n", i, data);
		}
		if (ctl_ops->rx_amp_enable)
			ctl_ops->rx_amp_enable(0);
		pr_aud_info("=============================================\n");
		break;
	case AIC3254_WRITE_REG:
		if (copy_from_user(&reg, (void *)argc,
					sizeof(CODEC_SPI_CMD)*2)) {
			pr_aud_err("%s: failed on copy_from_user\n", __func__);
			return -EFAULT;
		}
		pr_aud_info("%s: command list (%c,%02X,%02X) (%c,%02X,%02X)\n",
				__func__, reg[0].act, reg[0].reg, reg[0].data,
				reg[1].act, reg[1].reg, reg[1].data);
		aic3254_config_ex(reg, 2);
		break;
	case AIC3254_READ_REG:
		if (copy_from_user(&reg, (void *)argc,
					sizeof(CODEC_SPI_CMD)*2)) {
			pr_aud_err("%s: failed on copy_from_user\n", __func__);
			return -EFAULT;
		}
		if (ctl_ops->spibus_enable)
			ctl_ops->spibus_enable(1);
		for (i = 0; i < 2; i++) {
			if (reg[i].act == 'r' || reg[i].act == 'R')
				codec_spi_read(reg[i].reg, &reg[i].data);
			else if (reg[i].act == 'w' || reg[i].act == 'W')
				codec_spi_write(reg[i].reg, reg[i].data);
			else
				return -EINVAL;
		}
		if (ctl_ops->spibus_enable)
			ctl_ops->spibus_enable(0);
		if (copy_to_user((void *)argc, &reg, sizeof(CODEC_SPI_CMD)*2)) {
			pr_aud_err("%s: failed on copy_to_user\n", __func__);
			return -EFAULT;
		}
		break;
	case AIC3254_POWERDOWN:
		mutex_lock(&lock);
		aic3254_powerdown();
		mutex_unlock(&lock);
		break;
	case AIC3254_LOOPBACK:
		if (copy_from_user(&i, (void *)argc, sizeof(int))) {
			pr_aud_err("%s: failed on copy_from_user\n", __func__);
			return -EFAULT;
		}
		pr_aud_info("%s: index %d for LOOPBACK\n", __func__, i);
		aic3254_loopback(i);
		break;
	default:
		pr_aud_err("%s: invalid command %d\n", __func__, _IOC_NR(cmd));
		ret = -EINVAL;
	}

	return ret;
}
Ejemplo n.º 4
0
static int aic3254_config(CODEC_SPI_CMD *cmds, int size)
{
	int i, retry, ret;
	unsigned char data;

	if (ctl_ops->spibus_enable)
		ctl_ops->spibus_enable(1);

	if (!codec_dev) {
		pr_aud_err("%s: no spi device\n", __func__);
		return -EFAULT;
	}

	if (cmds == NULL) {
		pr_aud_err("%s: invalid spi parameters\n", __func__);
		return -EINVAL;
	}

	/* when LCM power is off, spi transmission would fail sometime */
	if (suspend_flag && ctl_ops->panel_sleep_in) {
		ret = ctl_ops->panel_sleep_in();
		suspend_flag = 0;
		if (ret < 0)
			pr_aud_err("%s: cannot make panel awake,"
				"it might failed on transmit SPI command\n"
				, __func__);
		else
			pr_aud_info("%s: success on invoking panel_sleep_in\n"
				, __func__);
	}
	/* large dsp image use bulk mode to transfer */
	/* avoid to bulk transfer on spi use ext_gpio_cs project */
	if (size < 1000) {
		for (i = 0; i < size; i++) {
			switch (cmds[i].act) {
			case 'w':
				codec_spi_write(cmds[i].reg, cmds[i].data);
				break;
			case 'r':
				for (retry = AIC3254_MAX_RETRY; retry > 0; retry--) {
					ret = codec_spi_read(cmds[i].reg, &data);
					if (ret < 0)
						pr_aud_err("%s: read fail %d, retry\n",
							__func__, ret);
					else if (data == cmds[i].data)
						break;
					msleep(1);
				}
				if (retry <= 0)
					pr_aud_info("3254 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 {
		spi_write_table(cmds, size);
	}
	if (ctl_ops->spibus_enable)
		ctl_ops->spibus_enable(0);
	return 0;
}
Ejemplo n.º 5
0
static int aic3254_config(CODEC_SPI_CMD *cmds, int size)
{
	int i, retry, ret;
	unsigned char data;

	if (!codec_dev) {
		pr_err("%s: no spi device\n", __func__);
		return -EFAULT;
	}

	if (cmds == NULL) {
		pr_err("%s: invalid spi parameters\n", __func__);
		return -EINVAL;
	} else
		pr_info("%s: size = %d\n", __func__, size);

	/* when LCM power is off, spi transmission would fail sometime */
	if (suspend_flag && ctl_ops->panel_sleep_in) {
		ret = ctl_ops->panel_sleep_in();
		suspend_flag = 0;
		if (ret < 0)
			pr_err("%s: cannot make panel awake,"
				"it might failed on transmit SPI command\n"
				, __func__);
		else
			pr_info("%s: success on invoking panel_sleep_in\n"
				, __func__);
	}


	for (i = 0; i < size; i++) {
		switch (cmds[i].act) {
		case 'w':
			codec_spi_write(cmds[i].reg, cmds[i].data);
			break;
		case 'r':
			for (retry = AIC3254_MAX_RETRY; retry > 0; retry--) {
				ret = codec_spi_read(cmds[i].reg, &data);
				if (ret < 0)
					pr_err("%s: read fail %d, retry\n",
						__func__, ret);
				else if (data == cmds[i].data)
					break;
				hr_msleep(10);
			}
			if (retry <= 0)
				pr_err("%s: 3254 power down procedure"
					" ,flag 0x%02X=0x%02X(0x%02X)\n",
					__func__, cmds[i].reg,
					ret, cmds[i].data);
			break;
		case 'd':
			hr_msleep(cmds[i].data);
			break;
		default:
			break;
		}
	}

	return 0;
}
static int32_t spi_write_table_parsepage(CODEC_SPI_CMD *cmds, int num)
{
	int i;
	int bulk_counter;
	int status = 0;
	struct spi_message	m;
	struct spi_transfer	tx_addr;
	bool is_page_zero = false;
	unsigned char page_select = 0x00;
	unsigned char book_select = 0x7F;
	unsigned int reg_long1, reg_long2;

	if (codec_spi_dev == NULL) {
		status = -ESHUTDOWN;
		return status;
	}

	i = 0;

	while (i < num - 1) {
		if (cmds[i].reg == book_select && is_page_zero) {
			/* select book */
			codec_spi_write(cmds[i].reg, cmds[i].data, false);
			i++;
		} else if (cmds[i].reg == page_select) {
			/* select page */
			if (cmds[i].data == 0x00) is_page_zero = true;
			else is_page_zero = false;
			codec_spi_write(cmds[i].reg, cmds[i].data, false);
			i++;
		} else {
			spi_message_init(&m);
			memset(bulk_tx, 0, MINIDSP_COL_MAX * 2 * \
				sizeof(uint8_t));
			memset(&tx_addr, 0, sizeof(struct spi_transfer));

			bulk_counter = 0;
			bulk_tx[bulk_counter] = cmds[i].reg << 1;
			bulk_tx[bulk_counter + 1] = cmds[i].data;
			bulk_counter += 2;

			do {
				reg_long1 = (unsigned int)cmds[i].reg;
				reg_long2 = (unsigned int)cmds[i+1].reg;
				if (reg_long2 == (reg_long1+1)) {
					bulk_tx[bulk_counter] = cmds[i+1].data;
					bulk_counter++;
				}
				i++;
			} while (reg_long2 == (reg_long1+1) && i < num-1);

			/*int j = 0;
			AUD_DBG("bulk_write : start reg: 0x%02X\n", bulk_tx[j] >> 1);
			for (j = 1; j < bulk_counter; j++)
				AUD_DBG("bulk_write : data: 0x%02X\n", bulk_tx[j]);
			AUD_DBG("bulk_counter = %d, i = %d\n", bulk_counter, i);*/

			tx_addr.tx_buf = bulk_tx;
			tx_addr.len = (bulk_counter);
			tx_addr.cs_change = 1;
			tx_addr.bits_per_word = 8;
			spi_message_add_tail(&tx_addr, &m);
			status = spi_sync(codec_spi_dev, &m);
		}
	}

	return status;
}
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;
}