/* 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; }
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(¶, (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(¶, (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(®, (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(®, (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, ®[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, ®, 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; }
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; }
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(¶, (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; }