static int esa_prepare_buffer(struct device *dev) { unsigned long iova; int n, ret; /* Original firmware */ si.fwmem = devm_kzalloc(dev, FWMEM_SIZE, GFP_KERNEL); if (!si.fwmem) { esa_err("Failed to alloc fwmem\n"); goto err; } si.fwmem_pa = virt_to_phys(si.fwmem); /* Firmware backup for SRAM */ si.fwmem_sram_bak = devm_kzalloc(dev, SRAM_FW_MAX, GFP_KERNEL); if (!si.fwmem_sram_bak) { esa_err("Failed to alloc fwmem\n"); goto err; } /* Firmware for DRAM */ for (n = 0; n < FWAREA_NUM; n++) { si.fwarea[n] = dma_alloc_coherent(dev, FWAREA_SIZE, &si.fwarea_pa[n], GFP_KERNEL); if (!si.fwarea[n]) { esa_err("Failed to alloc fwarea\n"); goto err0; } } for (n = 0, iova = FWAREA_IOVA; n < FWAREA_NUM; n++, iova += FWAREA_SIZE) { ret = iommu_map(si.domain, iova, si.fwarea_pa[n], FWAREA_SIZE, 0); if (ret) { esa_err("Failed to map iommu\n"); goto err1; } } /* Base address for IBUF, OBUF and FW LOG */ si.bufmem = si.fwarea[0] + BASEMEM_OFFSET; si.bufmem_pa = si.fwarea_pa[0]; si.fw_log_buf = si.sram + FW_LOG_ADDR; return 0; err1: for (n = 0, iova = FWAREA_IOVA; n < FWAREA_NUM; n++, iova += FWAREA_SIZE) { iommu_unmap(si.domain, iova, FWAREA_SIZE); } err0: for (n = 0; n < FWAREA_NUM; n++) { if (si.fwarea[n]) dma_free_coherent(dev, FWAREA_SIZE, si.fwarea[n], si.fwarea_pa[n]); } err: return -ENOMEM; }
static void esa_fw_request_complete(const struct firmware *fw_sram, void *ctx) { const struct firmware *fw_dram; struct device *dev = ctx; if (!fw_sram) { esa_err("Failed to requset firmware[%s]\n", FW_SRAM_NAME); return; } if (request_firmware(&fw_dram, FW_DRAM_NAME, dev)) { esa_err("Failed to requset firmware[%s]\n", FW_DRAM_NAME); return; } si.fwmem_loaded = true; si.fw_sbin_size = fw_sram->size; si.fw_dbin_size = fw_dram->size; memcpy(si.fwmem, fw_sram->data, si.fw_sbin_size); memcpy(si.fwmem + si.fw_sbin_size, fw_dram->data, si.fw_dbin_size); esa_info("FW Loaded (SRAM = %d, DRAM = %d)\n", si.fw_sbin_size, si.fw_dbin_size); return; }
static int esa_fw_startup(void) { #ifndef CONFIG_SND_SAMSUNG_SEIREN_OFFLOAD unsigned int dec_ver; #endif int ret; if (si.fw_ready) return 0; if (!si.fwmem_loaded) return -EAGAIN; /* power on */ si.fw_use_dram = true; esa_debug("Turn on CA5...\n"); esa_fw_download(); /* wait for fw ready */ ret = wait_event_interruptible_timeout(esa_wq, si.fw_ready, HZ / 2); if (!ret) { esa_err("%s: fw not ready!!!\n", __func__); si.fw_use_dram = false; return -EBUSY; } #ifndef CONFIG_SND_SAMSUNG_SEIREN_OFFLOAD /* check decoder version */ esa_send_cmd(SYS_GET_STATUS); dec_ver = readl(si.mailbox) & 0xFF00; dec_ver = dec_ver >> 8; esa_debug("Decoder version : %x\n", dec_ver); #endif return 0; }
static ssize_t esa_write(struct file *file, const char *buffer, size_t size, loff_t *pos) { int ret; mutex_lock(&esa_mutex); pm_runtime_get_sync(&si.pdev->dev); if (!si.fx_ext_on) { esa_debug("%s: fx ext not enabled\n", __func__); ret = -EINVAL; goto out; } if (!si.fx_work_buf) { esa_debug("%s: fx buf not ready\n", __func__); ret = -EBUSY; goto out; } if (copy_from_user(si.fx_work_buf, buffer, size)) { esa_err("%s: failed to copy_from_user\n", __func__); ret = -EFAULT; } else { esa_debug("%s: %lu bytes\n", __func__, size); ret = FX_BUF_SIZE; } out: pm_runtime_mark_last_busy(&si.pdev->dev); pm_runtime_put_sync_autosuspend(&si.pdev->dev); mutex_unlock(&esa_mutex); return ret; }
static int esa_open(struct inode *inode, struct file *file) { struct esa_rtd *rtd; if (!si.fwmem_loaded) { esa_err("Firmware not ready\n"); return -ENXIO; } /* alloc rtd */ rtd = esa_alloc_rtd(); if (!rtd) { esa_debug("%s: Not enough memory\n", __func__); return -EFAULT; } esa_debug("%s: idx:%d\n", __func__, rtd->idx); /* initialize */ file->private_data = rtd; rtd->get_eos = EOS_NO; rtd->need_config = false; rtd->select_ibuf = 0; rtd->select_obuf = 0; rtd->obuf0_filled = false; rtd->obuf1_filled = false; return 0; }
static int esa_send_cmd_(u32 cmd_code, bool sram_only) { u32 cnt, val; int ret; si.isr_done = 0; writel(cmd_code, si.mailbox + CMD_CODE); /* command */ writel(1, si.regs + SW_INTR_CA5); /* trigger ca5 */ si.fw_use_dram = sram_only ? false : true; ret = wait_event_interruptible_timeout(esa_wq, si.isr_done, HZ / 2); if (!ret) { esa_err("%s: CMD(%08X) timed out!!!\n", __func__, cmd_code); esa_dump_fw_log(); si.fw_use_dram = true; return -EBUSY; } cnt = msecs_to_loops(10); while (--cnt) { val = readl(si.regs + CA5_STATUS); if (val & CA5_STATUS_WFI) break; cpu_relax(); } si.fw_use_dram = false; return 0; }
static ssize_t esa_read(struct file *file, char *buffer, size_t size, loff_t *pos) { int ret; mutex_lock(&esa_mutex); pm_runtime_get_sync(&si.pdev->dev); if (!si.fx_ext_on) { esa_debug("%s: fx ext not enabled\n", __func__); ret = -EINVAL; goto out; } ret = wait_event_interruptible_timeout(esa_fx_wq, si.fx_irq_done, HZ); if (!ret) { esa_err("%s: fx irq timeout\n", __func__); ret = -EBUSY; goto out; } si.fx_irq_done = false; si.fx_work_buf = (unsigned char *)(si.sram + FX_BUF_OFFSET); si.fx_work_buf += si.fx_next_idx * FX_BUF_SIZE; esa_debug("%s: buf_idx = %d\n", __func__, si.fx_next_idx); if (copy_to_user((void *)buffer, si.fx_work_buf, FX_BUF_SIZE)) { esa_err("%s: failed to copy_to_user\n", __func__); ret = -EFAULT; } else { ret = FX_BUF_SIZE; } out: pm_runtime_mark_last_busy(&si.pdev->dev); pm_runtime_put_sync_autosuspend(&si.pdev->dev); mutex_unlock(&esa_mutex); return ret; }
int esa_dma_send_cmd(u32 cmd, int ch, void __iomem *reg_ack) { int n, ack; switch (cmd) { case CMD_DMA_PREPARE: case CMD_DMA_START: case CMD_DMA_STOP: break; default: esa_err("%s: unknown cmd %d\n", __func__, cmd); return -EIO; } spin_lock(&si.cmd_lock); writel(ch, si.mailbox + HANDLE_ID); /* dma ch */ writel(cmd, si.mailbox + CMD_CODE); /* command */ writel(1, si.regs + SW_INTR_CA5); /* trigger ca5 */ for (n = 0, ack = 0; n < 2000; n++) { if (readl(reg_ack)) { /* Wait for ACK */ ack = 1; break; } udelay(100); } writel(0, reg_ack); /* clear ACK */ spin_unlock(&si.cmd_lock); if (!ack) { esa_err("%s: No ack error!", __func__); esa_dump_fw_log(); } return 0; }
int esa_compr_send_buffer(const size_t copy_size, struct audio_processor *ap) { int ret; /* write mp3 data to firmware */ spin_lock(&si.compr_lock); writel(copy_size, si.mailbox + COMPR_SIZE_OF_FRAGMENT); ret = esa_compr_send_cmd(CMD_COMPR_WRITE, ap); if (ret) { esa_err("%s: can't send CMD_COMPR_WRITE (%d)\n", __func__, ret); spin_unlock(&si.compr_lock); return ret; } spin_unlock(&si.compr_lock); return 0; }
static int esa_remove(struct platform_device *pdev) { int ret = 0; free_irq(si.irq_ca5, 0); ret = misc_deregister(&esa_miscdev); if (ret) esa_err("Cannot deregister miscdev\n"); #ifndef CONFIG_PM_RUNTIME clk_disable_unprepare(si.clk_ca5); lpass_put_sync(&pdev->dev); #endif clk_put(si.clk_ca5); clk_put(si.opclk_ca5); #ifdef CONFIG_SND_SAMSUNG_SEIREN_DMA clk_put(si.clk_dmac); clk_put(si.clk_timer); #endif return ret; }
static long esa_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret = 0; mutex_lock(&esa_mutex); pm_runtime_get_sync(&si.pdev->dev); switch (cmd) { case SEIREN_IOCTL_FX_EXT: si.fx_ext_on = arg ? true : false; writel(si.fx_ext_on ? 1 : 0, si.mailbox + EFFECT_EXT_ON); break; default: esa_err("%s: unknown cmd:%08X, arg:%08X\n", __func__, cmd, (unsigned int)arg); break; } pm_runtime_mark_last_busy(&si.pdev->dev); pm_runtime_put_sync_autosuspend(&si.pdev->dev); mutex_unlock(&esa_mutex); return ret; }
int esa_compr_set_param(struct audio_processor* ap, uint8_t **buffer) { unsigned int ip_type; unsigned char *ibuf; u32 ibuf_ca5_pa; u32 ibuf_offset; int ret; ptr_ap = ap; /* initialize in buffer */ /* use free area in dram */ ibuf = si.fwarea[1]; ap->block_num = 1; /* calculate the physical address */ ibuf_offset = ibuf - si.fwarea[ap->block_num]; ibuf_ca5_pa = ibuf_offset + FWAREA_SIZE * ap->block_num; /* set buffer information at mailbox */ spin_lock(&si.lock); writel(ap->buffer_size, si.mailbox + COMPR_SIZE_OF_INBUF); writel(ibuf_ca5_pa, si.mailbox + COMPR_PHY_ADDR_INBUF); writel(ap->sample_rate, si.mailbox + COMPR_PARAM_SAMPLE); writel(ap->num_channels, si.mailbox + COMPR_PARAM_CH); ip_type = ap->codec_id << 16; writel(ip_type, si.mailbox + COMPR_IP_TYPE); spin_unlock(&si.lock); si.isr_compr_created = 0; ret = esa_compr_send_cmd(CMD_COMPR_SET_PARAM, ap); if (ret) { esa_err("%s: can't send CMD_COMPR_SET_PARAM (%d)\n", __func__, ret); return ret; } /* wait until the parameter is set up */ ret = wait_event_interruptible_timeout(esa_wq, si.isr_compr_created, HZ * 2); if (!ret) { esa_err("%s: compress set param timed out!!! (%d)\n", __func__, ret); writel(0, si.mailbox + COMPR_INTR_ACK); esa_dump_fw_log(); si.fw_use_dram = true; ptr_ap = NULL; return -EBUSY; } /* created instance */ ap->handle_id = readl(si.mailbox + COMPR_IP_ID); esa_info("%s: codec id:0x%x, ret_val:0x%x, handle_id:0x%x\n", __func__, (unsigned int)ap->codec_id, readl(si.mailbox + COMPR_RETURN_CMD), (unsigned int)ap->handle_id); /* return the buffer address for caller */ *buffer = ibuf; esa_info("%s: allocated buffer address (0x%p), size(0x%x)\n", __func__, *buffer, ap->buffer_size); #ifdef CONFIG_SND_ESA_SA_EFFECT si.effect_on = false; #endif return 0; }
static irqreturn_t esa_isr(int irqno, void *id) { unsigned int fw_stat, log_size, val; bool wakeup = false; #ifdef CONFIG_SND_SAMSUNG_SEIREN_OFFLOAD unsigned int size; int ret; #endif writel(0, si.regs + SW_INTR_CPU); val = readl(si.mailbox + RETURN_CMD); if (val == 1) esa_err("%s: There is possibility of firmware CMD fail %u\n", __func__, val); fw_stat = val >> 16; esa_debug("fw_stat(%08x), val(%08x)\n", fw_stat, val); switch (fw_stat) { case INTR_WAKEUP: /* handle wakeup interrupt from firmware */ si.isr_done = 1; wakeup = true; break; case INTR_READY: pr_debug("FW is ready!\n"); si.fw_ready = true; wakeup = true; break; #ifdef CONFIG_SND_SAMSUNG_SEIREN_DMA case INTR_DMA: val &= 0xFF; esa_debug("INTR_DMA (ch %d)\n", val); if (si.dma_cb[val]) (*si.dma_cb[val])(si.dma_cb_param[val]); writel(0, si.mailbox + COMPR_INTR_DMA_ACK); break; #endif #ifdef CONFIG_SND_SAMSUNG_SEIREN_OFFLOAD case INTR_CREATED: esa_debug("INTR_CREATED\n"); si.isr_compr_created = 1; wakeup = true; writel(0, si.mailbox + COMPR_INTR_ACK); break; case INTR_DECODED: /* check the error */ val &= 0xFF; if (val) { esa_err("INTR_DECODED err(%d)\n", val); esa_dump_fw_log(); writel(0, si.mailbox + COMPR_INTR_ACK); break; } /* update copied total bytes */ size = readl(si.mailbox + COMPR_SIZE_OUT_DATA); esa_debug("INTR_DECODED(%d)\n", size); ret = ptr_ap->ops(fw_stat, size, ptr_ap->priv); if (ret) esa_err("INTR_DECODED handler err(%d)\n", ret); writel(0, si.mailbox + COMPR_INTR_ACK); break; case INTR_FLUSH: /* check the error */ val &= 0xFF; if (val) { esa_err("INTR_FLUSH err(%d)\n", val); esa_dump_fw_log(); writel(0, si.mailbox + COMPR_INTR_ACK); break; } /* flush done */ ret = ptr_ap->ops(fw_stat, 0, ptr_ap->priv); if (ret) esa_err("INTR_FLUSH handler err(%d)\n", ret); writel(0, si.mailbox + COMPR_INTR_ACK); break; case INTR_PAUSED: /* check the error */ val &= 0xFF; if (val) { esa_err("INTR_PAUSED err(%d)\n", val); esa_dump_fw_log(); writel(0, si.mailbox + COMPR_INTR_ACK); break; } /* paused */ ret = ptr_ap->ops(fw_stat, 0, ptr_ap->priv); if (ret) esa_err("INTR_PAUSED handler err(%d)\n", ret); writel(0, si.mailbox + COMPR_INTR_ACK); break; case INTR_EOS: ret = ptr_ap->ops(fw_stat, 0, ptr_ap->priv); if (ret) esa_err("INTR_EOS handler err(%d)\n", ret); writel(0, si.mailbox + COMPR_INTR_ACK); break; case INTR_DESTROY: /* check the error */ val &= 0xFF; if (val) { esa_err("INTR_DESTROY err(%d)\n", val); esa_dump_fw_log(); writel(0, si.mailbox + COMPR_INTR_ACK); break; } /* destroied */ ret = ptr_ap->ops(fw_stat, 0, ptr_ap->priv); if (ret) esa_err("INTR_DESTROY handler err(%d)\n", ret); writel(0, si.mailbox + COMPR_INTR_ACK); break; case INTR_FX_EXT: esa_info("INTR_FX_EXT: fw_stat(%08x), val(%08x)\n", fw_stat, val); si.fx_next_idx = val & 0xF; si.fx_irq_done = true; if (waitqueue_active(&esa_fx_wq)) wake_up_interruptible(&esa_fx_wq); writel(0, si.mailbox + COMPR_INTR_ACK); break; #endif case INTR_FW_LOG: /* print debug message from firmware */ log_size = readl(si.mailbox + RETURN_CMD) & 0x00FF; if (!log_size) { esa_debug("FW: %s", si.fw_log); si.fw_log_pos = 0; } else { val = readl(si.mailbox + FW_LOG_VAL1); memcpy(si.fw_log + si.fw_log_pos, &val, 4); val = readl(si.mailbox + FW_LOG_VAL2); memcpy(si.fw_log + si.fw_log_pos + 4, &val, 4); si.fw_log_pos += log_size; si.fw_log[si.fw_log_pos] = '\0'; } writel(0, si.mailbox + RETURN_CMD); break; default: esa_err("%s: unknown intr_stat = 0x%08X\n", __func__, fw_stat); break; } if (wakeup && waitqueue_active(&esa_wq)) wake_up_interruptible(&esa_wq); return IRQ_HANDLED; }
static int esa_set_params(struct file *file, unsigned int param, unsigned long arg) { struct esa_rtd *rtd = file->private_data; int ret = 0; switch (param) { case PCM_PARAM_MAX_SAMPLE_RATE: esa_debug("PCM_PARAM_MAX_SAMPLE_RATE: arg:%ld\n", arg); break; case PCM_PARAM_MAX_NUM_OF_CH: esa_debug("PCM_PARAM_MAX_NUM_OF_CH: arg:%ld\n", arg); break; case PCM_PARAM_MAX_BIT_PER_SAMPLE: esa_debug("PCM_PARAM_MAX_BIT_PER_SAMPLE: arg:%ld\n", arg); break; case PCM_PARAM_SAMPLE_RATE: esa_debug("%s: sampling rate: %ld\n", __func__, arg); writel(rtd->handle_id, si.mailbox + HANDLE_ID); writel((unsigned int)arg, si.mailbox + PARAMS_VAL1); esa_send_cmd((param << 16) | CMD_SET_PARAMS); break; case PCM_PARAM_NUM_OF_CH: esa_debug("%s: channel: %ld\n", __func__, arg); writel(rtd->handle_id, si.mailbox + HANDLE_ID); writel((unsigned int)arg, si.mailbox + PARAMS_VAL1); esa_send_cmd((param << 16) | CMD_SET_PARAMS); break; case PCM_PARAM_BIT_PER_SAMPLE: esa_debug("PCM_PARAM_BIT_PER_SAMPLE: arg:%ld\n", arg); break; case PCM_MAX_CONFIG_INFO: esa_debug("PCM_MAX_CONFIG_INFO: arg:%ld\n", arg); break; case PCM_CONFIG_INFO: esa_debug("PCM_CONFIG_INFO: arg:%ld\n", arg); break; case EQ_PARAM_NUM_OF_PRESETS: esa_debug("EQ_PARAM_NUM_OF_PRESETS: arg:%ld\n", arg); break; case EQ_PARAM_MAX_NUM_OF_BANDS: esa_debug("EQ_PARAM_MAX_NUM_OF_BANDS: arg:%ld\n", arg); break; case EQ_PARAM_RANGE_OF_BANDLEVEL: esa_debug("EQ_PARAM_RANGE_OF_BANDLEVEL: arg:%ld\n", arg); break; case EQ_PARAM_RANGE_OF_FREQ: esa_debug("EQ_PARAM_RANGE_OF_FREQ: arg:%ld\n", arg); break; case EQ_PARAM_PRESET_ID: esa_debug("%s: eq preset: %ld\n", __func__, arg); writel(rtd->handle_id, si.mailbox + HANDLE_ID); writel((unsigned int)arg, si.mailbox + PARAMS_VAL1); esa_send_cmd((param << 16) | CMD_SET_PARAMS); break; case EQ_PARAM_NUM_OF_BANDS: esa_debug("EQ_PARAM_NUM_OF_BANDS: arg:%ld\n", arg); break; case EQ_PARAM_CENTER_FREQ: esa_debug("EQ_PARAM_CENTER_FREQ: arg:%ld\n", arg); break; case EQ_PARAM_BANDLEVEL: esa_debug("EQ_PARAM_BANDLEVEL: arg:%ld\n", arg); break; case EQ_PARAM_BANDWIDTH: esa_debug("EQ_PARAM_BANDWIDTH: arg:%ld\n", arg); break; case EQ_MAX_CONFIG_INFO: esa_debug("EQ_MAX_CONFIG_INFO: arg:%ld\n", arg); break; case EQ_CONFIG_INFO: esa_debug("EQ_CONFIG_INFO: arg:%ld\n", arg); break; case EQ_BAND_INFO: esa_debug("EQ_BAND_INFO: arg:%ld\n", arg); break; case ADEC_PARAM_SET_EOS: writel(rtd->handle_id, si.mailbox + HANDLE_ID); esa_send_cmd((param << 16) | CMD_SET_PARAMS); esa_debug("ADEC_PARAM_SET_EOS: handle_id:%x\n", rtd->handle_id); rtd->get_eos = EOS_YET; break; case SET_IBUF_POOL_INFO: esa_debug("SET_IBUF_POOL_INFO: arg:%ld\n", arg); break; case SET_OBUF_POOL_INFO: esa_debug("SET_OBUF_POOL_INFO: arg:%ld\n", arg); break; default: esa_err("%s: Unknown %ld\n", __func__, arg); break; } return ret; }
static ssize_t esa_read(struct file *file, char *buffer, size_t size, loff_t *pos) { struct esa_rtd *rtd = file->private_data; unsigned char *obuf; unsigned int *obuf_filled_size; bool *obuf_filled; unsigned char *obuf_; unsigned int *obuf_filled_size_; bool *obuf_filled_; mutex_lock(&esa_mutex); pm_runtime_get_sync(&si.pdev->dev); /* select OBUF0 or OBUF1 */ if (rtd->select_obuf == 0) { obuf = rtd->obuf0; obuf_filled = &rtd->obuf0_filled; obuf_filled_size = &rtd->obuf0_filled_size; obuf_ = rtd->obuf1; obuf_filled_ = &rtd->obuf1_filled; obuf_filled_size_ = &rtd->obuf1_filled_size; esa_debug("%s: use obuf0\n", __func__); } else { obuf = rtd->obuf1; obuf_filled = &rtd->obuf1_filled; obuf_filled_size = &rtd->obuf1_filled_size; obuf_ = rtd->obuf0; obuf_filled_ = &rtd->obuf0_filled; obuf_filled_size_ = &rtd->obuf0_filled_size; esa_debug("%s: use obuf1\n", __func__); } /* select OBUF0 or OBUF1 for next reading */ rtd->select_obuf = !rtd->select_obuf; /* later... invalidate obuf cache */ /* send pcm data to user */ if (copy_to_user((void *)buffer, obuf, *obuf_filled_size)) { esa_err("%s: failed to copy_to_user\n", __func__); goto err; } /* if meet eos, it sholud also collect data of another buff */ if (rtd->get_eos && !*obuf_filled_) { rtd->get_eos = EOS_FINAL; } esa_debug("%s: handle_id[%x], idx:[%d], obuf:[%d], obuf_filled_size:[%d]\n", __func__, rtd->handle_id, rtd->idx, !rtd->select_obuf, (u32)*obuf_filled_size); *obuf_filled = false; pm_runtime_mark_last_busy(&si.pdev->dev); pm_runtime_put_sync_autosuspend(&si.pdev->dev); mutex_unlock(&esa_mutex); return *obuf_filled_size; err: pm_runtime_mark_last_busy(&si.pdev->dev); pm_runtime_put_sync_autosuspend(&si.pdev->dev); mutex_unlock(&esa_mutex); return -EFAULT; }
static ssize_t esa_write(struct file *file, const char *buffer, size_t size, loff_t *pos) { struct esa_rtd *rtd = file->private_data; unsigned char *ibuf; unsigned char *obuf; unsigned int *obuf_filled_size; bool *obuf_filled; int response, consumed_size = 0; mutex_lock(&esa_mutex); pm_runtime_get_sync(&si.pdev->dev); if (rtd->obuf0_filled && rtd->obuf1_filled) { esa_err("%s: There is no unfilled obuf\n", __func__); goto err; } /* select IBUF0 or IBUF1 */ if (rtd->select_ibuf == 0) { ibuf = rtd->ibuf0; obuf = rtd->obuf0; obuf_filled = &rtd->obuf0_filled; obuf_filled_size = &rtd->obuf0_filled_size; esa_debug("%s: use ibuf0\n", __func__); } else { ibuf = rtd->ibuf1; obuf = rtd->obuf1; obuf_filled = &rtd->obuf1_filled; obuf_filled_size = &rtd->obuf1_filled_size; esa_debug("%s: use ibuf1\n", __func__); } /* receive stream data from user */ if (copy_from_user(ibuf, buffer, size)) { esa_err("%s: failed to copy_from_user\n", __func__); goto err; } /* select IBUF0 or IBUF1 for next writing */ rtd->select_ibuf = !rtd->select_ibuf; /* send execute command to FW for decoding */ response = esa_send_cmd_exe(rtd, ibuf, obuf, size); /* filled size in OBUF */ *obuf_filled_size = readl(si.mailbox + SIZE_OUT_DATA); /* consumed size */ consumed_size = readl(si.mailbox + CONSUMED_BYTE_IN); if (response == 0 && *obuf_filled_size > 0) { *obuf_filled = true; } else { if (consumed_size <= 0) consumed_size = response; if (rtd->need_config) rtd->need_config = false; else if (size != 0) esa_debug("%s: No output? response:%x\n", __func__, response); } pm_runtime_mark_last_busy(&si.pdev->dev); pm_runtime_put_sync_autosuspend(&si.pdev->dev); mutex_unlock(&esa_mutex); esa_debug("%s: handle_id[%x], idx:[%d], consumed:[%d], filled_size:[%d], ibuf:[%d]\n", __func__, rtd->handle_id, rtd->idx, consumed_size, *obuf_filled_size, !rtd->select_ibuf); return consumed_size; err: pm_runtime_mark_last_busy(&si.pdev->dev); pm_runtime_put_sync_autosuspend(&si.pdev->dev); mutex_unlock(&esa_mutex); return -EFAULT; }
int esa_compr_send_cmd(int32_t cmd, struct audio_processor* ap) { int n, ack; switch (cmd) { case CMD_COMPR_CREATE: esa_info("%s: CMD_COMPR_CREATE %d\n", __func__, cmd); break; case CMD_COMPR_DESTROY: esa_info("%s: CMD_COMPR_DESTROY %d\n", __func__, cmd); break; case CMD_COMPR_SET_PARAM: esa_info("%s: CMD_COMPR_SET_PARAM %d\n", __func__, cmd); break; case CMD_COMPR_WRITE: esa_debug("%s: CMD_COMPR_WRITE %d\n", __func__, cmd); break; case CMD_COMPR_READ: esa_debug("%s: CMD_COMPR_READ %d\n", __func__, cmd); break; case CMD_COMPR_START: esa_info("%s: CMD_COMPR_START %d\n", __func__, cmd); break; case CMD_COMPR_STOP: esa_info("%s: CMD_COMPR_STOP %d\n", __func__, cmd); break; case CMD_COMPR_PAUSE: esa_info("%s: CMD_COMPR_PAUSE %d\n", __func__, cmd); break; case CMD_COMPR_EOS: esa_info("%s: CMD_COMPR_EOS %d\n", __func__, cmd); break; case CMD_COMPR_GET_VOLUME: esa_debug("%s: CMD_COMPR_GET_VOLUME %d\n", __func__, cmd); break; case CMD_COMPR_SET_VOLUME: esa_debug("%s: CMD_COMPR_SET_VOLUME %d\n", __func__, cmd); break; default: esa_err("%s: unknown cmd %d\n", __func__, cmd); return -EIO; } spin_lock(&si.cmd_lock); writel(ap->handle_id, si.mailbox + COMPR_HANDLE_ID); writel(cmd, si.mailbox + COMPR_CMD_CODE); /* command */ writel(1, si.regs + SW_INTR_CA5); /* trigger ca5 */ for (n = 0, ack = 0; n < 2000; n++) { if (readl(ap->reg_ack)) { /* Wait for ACK */ ack = 1; break; } udelay(100); } writel(0, ap->reg_ack); /* clear ACK */ spin_unlock(&si.cmd_lock); if (!ack) { esa_err("%s: No ack error!(%x)", __func__, cmd); esa_dump_fw_log(); return -EFAULT; } return 0; }
static int esa_exe(struct file *file, unsigned int param, unsigned long arg) { struct esa_rtd *rtd = file->private_data; struct audio_mem_info_t ibuf_info, obuf_info; int response, obuf_filled_size; unsigned char *ibuf = rtd->ibuf0; unsigned char *obuf = rtd->obuf0; int ret = 0; mutex_lock(&esa_mutex); /* receive ibuf_info from user */ if (copy_from_user(&ibuf_info, (struct audio_mem_info_t *)arg, sizeof(struct audio_mem_info_t))) { esa_err("%s: failed to copy_from_user ibuf_info\n", __func__); goto err; } /* receive obuf_info from user */ arg += sizeof(struct audio_mem_info_t); if (copy_from_user(&obuf_info, (struct audio_mem_info_t *)arg, sizeof(struct audio_mem_info_t))) { esa_err("%s: failed to copy_from_user obuf_info\n", __func__); goto err; } /* prevent test alarmed about tainted data */ if (ibuf_info.mem_size > rtd->ibuf_size) { ibuf_info.mem_size = rtd->ibuf_size; esa_err("%s: There is too much input data", __func__); } /* receive pcm data from user */ if (copy_from_user(ibuf, (void *)(u64)ibuf_info.virt_addr, ibuf_info.mem_size)) { esa_err("%s: failed to copy_from_user\n", __func__); goto err; } /* send execute command to FW for decoding */ response = esa_send_cmd_exe(rtd, ibuf, obuf, ibuf_info.mem_size); /* filled size in OBUF */ obuf_filled_size = readl(si.mailbox + SIZE_OUT_DATA); /* later... invalidate obuf cache */ if (!response && obuf_filled_size > 0) { esa_debug("%s: cmd_exe OK!\n", __func__); /* send pcm data to user */ if (copy_to_user((void *)(u64)obuf_info.virt_addr, obuf, obuf_filled_size)) { esa_err("%s: failed to copy_to_user obuf pcm\n", __func__); goto err; } } else { esa_debug("%s: cmd_exe Fail: %d\n", __func__, response); } esa_debug("%s: handle_id[%x], idx:[%d], filled_size:[%d]\n", __func__, rtd->handle_id, rtd->idx, obuf_filled_size); return ret; err: mutex_unlock(&esa_mutex); return -EFAULT; }
static int esa_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = pdev->dev.of_node; struct resource *res; int ret = 0; printk(banner); spin_lock_init(&si.lock); #if defined(CONFIG_SND_SAMSUNG_SEIREN_DMA) || \ defined(CONFIG_SND_SAMSUNG_SEIREN_OFFLOAD) spin_lock_init(&si.cmd_lock); spin_lock_init(&si.compr_lock); #endif si.pdev = pdev; ret = misc_register(&esa_miscdev); if (ret) { esa_err("Cannot register miscdev\n"); return -ENODEV; } si.regs = lpass_get_regs(); if (!si.regs) { esa_err("Failed to get lpass regs\n"); return -ENODEV; } si.sram = lpass_get_mem(); if (!si.sram) { esa_err("Failed to get lpass sram\n"); return -ENODEV; } #ifdef CONFIG_SND_SAMSUNG_SEIREN_DMA si.dma_param = si.sram + DMA_PARAM_OFFSET; #endif #ifdef CONFIG_SND_ESA_SA_EFFECT si.effect_ram = si.sram + EFFECT_OFFSET; #endif res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { dev_err(dev, "Failed to get irq resource\n"); return -ENXIO; } si.irq_ca5 = res->start; si.mailbox = si.regs + 0x80; si.clk_ca5 = clk_get(dev, "ca5"); if (IS_ERR(si.clk_ca5)) { dev_err(dev, "ca5 clk not found\n"); return -ENODEV; } si.opclk_ca5 = clk_get(dev, "ca5_opclk"); if (IS_ERR(si.opclk_ca5)) { dev_err(dev, "ca5 opclk not found\n"); clk_put(si.clk_ca5); return -ENODEV; } #ifdef CONFIG_SND_SAMSUNG_SEIREN_DMA si.clk_dmac = clk_get(dev, "dmac"); if (IS_ERR(si.clk_dmac)) { dev_err(dev, "dmac clk not found\n"); clk_put(si.opclk_ca5); clk_put(si.clk_ca5); return -ENODEV; } si.clk_timer = clk_get(dev, "timer"); if (IS_ERR(si.clk_timer)) { dev_err(dev, "timer clk not found\n"); clk_put(si.clk_dmac); clk_put(si.opclk_ca5); clk_put(si.clk_ca5); return -ENODEV; } #endif if (np) { if (of_find_property(np, "samsung,lpass-subip", NULL)) lpass_register_subip(dev, "ca5"); } #ifdef CONFIG_SND_SAMSUNG_IOMMU si.domain = lpass_get_iommu_domain(); if (!si.domain) { dev_err(dev, "iommu not available\n"); goto err; } /* prepare buffer */ if (esa_prepare_buffer(dev)) goto err; #else dev_err(dev, "iommu not available\n"); goto err; #endif ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, FW_SRAM_NAME, dev, GFP_KERNEL, dev, esa_fw_request_complete); if (ret) { dev_err(dev, "could not load firmware\n"); goto err; } ret = request_irq(si.irq_ca5, esa_isr, 0, "lpass-ca5", 0); if (ret < 0) { esa_err("Failed to claim CA5 irq\n"); goto err; } #ifdef CONFIG_PROC_FS si.proc_file = proc_create("driver/seiren", 0, NULL, &esa_proc_fops); if (!si.proc_file) esa_info("Failed to register /proc/driver/seiren\n"); #endif /* hold reset */ lpass_reset(LPASS_IP_CA5, LPASS_OP_RESET); esa_debug("regs = %p\n", si.regs); esa_debug("mailbox = %p\n", si.mailbox); esa_debug("bufmem_pa = %p\n", (void*)si.bufmem_pa); esa_debug("fwmem_pa = %p\n", (void*)si.fwmem_pa); esa_debug("ca5 opclk = %ldHz\n", clk_get_rate(si.opclk_ca5)); #ifdef CONFIG_PM_RUNTIME pm_runtime_use_autosuspend(dev); pm_runtime_set_autosuspend_delay(dev, 300); pm_runtime_enable(dev); pm_runtime_get_sync(dev); pm_runtime_put_sync(dev); #else esa_do_resume(dev); #endif #ifdef CONFIG_PM_DEVFREQ si.mif_qos = 0; si.int_qos = 0; pm_qos_add_request(&si.ca5_mif_qos, PM_QOS_BUS_THROUGHPUT, 0); pm_qos_add_request(&si.ca5_int_qos, PM_QOS_DEVICE_THROUGHPUT, 0); #endif return 0; err: #ifdef CONFIG_SND_SAMSUNG_SEIREN_DMA clk_put(si.clk_timer); clk_put(si.clk_dmac); #endif clk_put(si.opclk_ca5); clk_put(si.clk_ca5); return -ENODEV; }
static int compr_free(struct snd_compr_stream *cstream) { struct snd_compr_runtime *runtime = cstream->runtime; struct runtime_data *prtd = runtime->private_data; struct snd_pcm_substream *substream; struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; const struct snd_soc_dai_ops *cpu_dai_ops; const struct snd_soc_dai_ops *codec_dai_ops; unsigned long flags; int ret; #ifdef AUDIO_PERF u64 playback_time, total_time = 0; int idx; #endif pr_debug("%s\n", __func__); if (!prtd) { pr_info("compress dai has already freed.\n"); return 0; } substream = &prtd->substream; cpu_dai = prtd->cpu_dai; codec_dai = prtd->codec_dai; cpu_dai_ops = cpu_dai->driver->ops; codec_dai_ops = codec_dai->driver->ops; if (atomic_read(&prtd->eos)) { /* ALSA Framework callback to notify drain complete */ snd_compr_drain_notify(cstream); atomic_set(&prtd->eos, 0); pr_debug("%s Call Drain notify to wakeup\n", __func__); } if (atomic_read(&prtd->created)) { spin_lock_irqsave(&prtd->lock, flags); atomic_set(&prtd->created, 0); prtd->exit_ack = 0; ret = esa_compr_send_cmd(CMD_COMPR_DESTROY, prtd->ap); if (ret) { esa_err("%s: can't send CMD_COMPR_DESTROY (%d)\n", __func__, ret); spin_unlock_irqrestore(&prtd->lock, flags); } else { spin_unlock_irqrestore(&prtd->lock, flags); ret = wait_event_interruptible_timeout(prtd->exit_wait, prtd->exit_ack, 1 * HZ); if (!ret) pr_err("%s: CMD_DESTROY timed out!!!\n", __func__); } } #ifdef CONFIG_SND_ESA_SA_EFFECT aud_vol.ap[COMPR_DAI_MULTIMEDIA_1] = NULL; #endif esa_compr_set_state(false); /* codec hw_free -> cpu hw_free -> cpu shutdown -> codec shutdown */ if (codec_dai_ops->hw_free) (*codec_dai_ops->hw_free)(substream, codec_dai); if (cpu_dai_ops->hw_free) (*cpu_dai_ops->hw_free)(substream, cpu_dai); if (cpu_dai_ops->shutdown) (*cpu_dai_ops->shutdown)(substream, cpu_dai); if (codec_dai_ops->shutdown) (*codec_dai_ops->shutdown)(substream, codec_dai); if (substream->runtime) kfree(substream->runtime->hw_constraints.rules); kfree(substream->runtime); esa_compr_close(); #ifdef AUDIO_PERF prtd->end_time[OPEN_T] = sched_clock(); playback_time = prtd->end_time[OPEN_T] - prtd->start_time[OPEN_T]; for (idx = 0; idx < TOTAL_TIMES; idx++) { total_time += prtd->total_time[idx]; } pr_debug("%s: measure the audio waken time : %llu\n", __func__, total_time); pr_debug("%s: may be the ap sleep time : (%llu/%llu)\n", __func__, playback_time - total_time, playback_time); #endif kfree(prtd->ap); kfree(prtd); return 0; }
static int esa_get_params(struct file *file, unsigned int param, unsigned long arg) { struct esa_rtd *rtd = file->private_data; struct audio_mem_info_t *argp = (struct audio_mem_info_t *)arg; struct audio_pcm_config_info_t *argp_pcm_info; unsigned long val; int ret = 0; switch (param) { case PCM_PARAM_MAX_SAMPLE_RATE: esa_debug("PCM_PARAM_MAX_SAMPLE_RATE: arg:%ld\n", arg); break; case PCM_PARAM_MAX_NUM_OF_CH: esa_debug("PCM_PARAM_MAX_NUM_OF_CH: arg:%ld\n", arg); break; case PCM_PARAM_MAX_BIT_PER_SAMPLE: esa_debug("PCM_PARAM_MAX_BIT_PER_SAMPLE: arg:%ld\n", arg); break; case PCM_PARAM_SAMPLE_RATE: writel(rtd->handle_id, si.mailbox + HANDLE_ID); esa_send_cmd((param << 16) | CMD_GET_PARAMS); val = readl(si.mailbox + PARAMS_VAL1); esa_debug("%s: sampling rate:%ld\n", __func__, val); if (val >= SAMPLE_RATE_MIN) { esa_debug("SAMPLE_RATE: SUCCESS: arg:0x%p\n", (void*)arg); ret = copy_to_user((unsigned long *)arg, &val, sizeof(unsigned long)); } else esa_debug("SAMPLE_RATE: FAILED: arg:0x%p\n", (void*)arg); break; case PCM_PARAM_NUM_OF_CH: writel(rtd->handle_id, si.mailbox + HANDLE_ID); esa_send_cmd((param << 16) | CMD_GET_PARAMS); val = readl(si.mailbox + PARAMS_VAL1); esa_debug("%s: Channel:%ld\n", __func__, val); if (val >= CH_NUM_MIN) { esa_debug("PCM_CONFIG_NUM_CH: SUCCESS: arg:0x%p\n", (void*)arg); ret = copy_to_user((unsigned long *)arg, &val, sizeof(unsigned long)); } else esa_debug("PCM_PARAM_NUM_CH: FAILED: arg:0x%p\n", (void*)arg); break; case PCM_PARAM_BIT_PER_SAMPLE: esa_debug("PCM_PARAM_BIT_PER_SAMPLE: arg:%ld\n", arg); break; case PCM_MAX_CONFIG_INFO: esa_debug("PCM_MAX_CONFIG_INFO: arg:%ld\n", arg); break; case PCM_CONFIG_INFO: argp_pcm_info = (struct audio_pcm_config_info_t *)arg; rtd->pcm_info.sample_rate = readl(si.mailbox + FREQ_SAMPLE); rtd->pcm_info.num_of_channel = readl(si.mailbox + CH_NUM); esa_debug("%s: rate:%d, ch:%d\n", __func__, rtd->pcm_info.sample_rate, rtd->pcm_info.num_of_channel); if (rtd->pcm_info.sample_rate >= 8000 && rtd->pcm_info.num_of_channel > 0) { esa_debug("PCM_CONFIG_INFO: SUCCESS: arg:0x%p\n", (void*)arg); ret = copy_to_user(argp_pcm_info, &rtd->ibuf_info, sizeof(struct audio_pcm_config_info_t)); } else esa_debug("PCM_CONFIG_INFO: FAILED: arg:0x%p\n", (void*)arg); break; case ADEC_PARAM_GET_OUTPUT_STATUS: writel(rtd->handle_id, si.mailbox + HANDLE_ID); esa_send_cmd((param << 16) | CMD_GET_PARAMS); val = readl(si.mailbox + PARAMS_VAL1); esa_debug("OUTPUT_STATUS:%ld, handle_id:%x\n", val, rtd->handle_id); if (val && rtd->get_eos == EOS_YET) val = 0; ret = copy_to_user((unsigned long *)arg, &val, sizeof(unsigned long)); break; case GET_IBUF_POOL_INFO: esa_debug("GET_IBUF_POOL_INFO: arg:0x%p\n", (void*)arg); rtd->ibuf_info.mem_size = rtd->ibuf_size; rtd->ibuf_info.block_count = rtd->ibuf_count; ret = copy_to_user(argp, &rtd->ibuf_info, sizeof(struct audio_mem_info_t)); break; case GET_OBUF_POOL_INFO: esa_debug("GET_OBUF_POOL_INFO: arg:0x%p\n", (void*)arg); rtd->obuf_info.mem_size = rtd->obuf_size; rtd->obuf_info.block_count = rtd->obuf_count; ret = copy_to_user(argp, &rtd->obuf_info, sizeof(struct audio_mem_info_t)); break; default: esa_err("%s: Unknown %ld\n", __func__, arg); break; } if (ret) esa_err("%s: failed to copy_to_user\n", __func__); return ret; }