| /* | |
| * Driver for the Conexant CX25821 PCIe bridge | |
| * | |
| * Copyright (C) 2009 Conexant Systems Inc. | |
| * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com> | |
| * | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation; either version 2 of the License, or | |
| * (at your option) any later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * | |
| * GNU General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program; if not, write to the Free Software | |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| */ | |
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
| #include "cx25821-video.h" | |
| #include "cx25821-audio-upstream.h" | |
| #include <linux/fs.h> | |
| #include <linux/errno.h> | |
| #include <linux/kernel.h> | |
| #include <linux/init.h> | |
| #include <linux/module.h> | |
| #include <linux/syscalls.h> | |
| #include <linux/file.h> | |
| #include <linux/fcntl.h> | |
| #include <linux/delay.h> | |
| #include <linux/slab.h> | |
| #include <linux/uaccess.h> | |
| MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); | |
| MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>"); | |
| MODULE_LICENSE("GPL"); | |
| static int _intr_msk = FLD_AUD_SRC_RISCI1 | FLD_AUD_SRC_OF | | |
| FLD_AUD_SRC_SYNC | FLD_AUD_SRC_OPC_ERR; | |
| static int cx25821_sram_channel_setup_upstream_audio(struct cx25821_dev *dev, | |
| const struct sram_channel *ch, | |
| unsigned int bpl, u32 risc) | |
| { | |
| unsigned int i, lines; | |
| u32 cdt; | |
| if (ch->cmds_start == 0) { | |
| cx_write(ch->ptr1_reg, 0); | |
| cx_write(ch->ptr2_reg, 0); | |
| cx_write(ch->cnt2_reg, 0); | |
| cx_write(ch->cnt1_reg, 0); | |
| return 0; | |
| } | |
| bpl = (bpl + 7) & ~7; /* alignment */ | |
| cdt = ch->cdt; | |
| lines = ch->fifo_size / bpl; | |
| if (lines > 3) | |
| lines = 3; | |
| BUG_ON(lines < 2); | |
| /* write CDT */ | |
| for (i = 0; i < lines; i++) { | |
| cx_write(cdt + 16 * i, ch->fifo_start + bpl * i); | |
| cx_write(cdt + 16 * i + 4, 0); | |
| cx_write(cdt + 16 * i + 8, 0); | |
| cx_write(cdt + 16 * i + 12, 0); | |
| } | |
| /* write CMDS */ | |
| cx_write(ch->cmds_start + 0, risc); | |
| cx_write(ch->cmds_start + 4, 0); | |
| cx_write(ch->cmds_start + 8, cdt); | |
| cx_write(ch->cmds_start + 12, AUDIO_CDT_SIZE_QW); | |
| cx_write(ch->cmds_start + 16, ch->ctrl_start); | |
| /* IQ size */ | |
| cx_write(ch->cmds_start + 20, AUDIO_IQ_SIZE_DW); | |
| for (i = 24; i < 80; i += 4) | |
| cx_write(ch->cmds_start + i, 0); | |
| /* fill registers */ | |
| cx_write(ch->ptr1_reg, ch->fifo_start); | |
| cx_write(ch->ptr2_reg, cdt); | |
| cx_write(ch->cnt2_reg, AUDIO_CDT_SIZE_QW); | |
| cx_write(ch->cnt1_reg, AUDIO_CLUSTER_SIZE_QW - 1); | |
| return 0; | |
| } | |
| static __le32 *cx25821_risc_field_upstream_audio(struct cx25821_dev *dev, | |
| __le32 *rp, | |
| dma_addr_t databuf_phys_addr, | |
| unsigned int bpl, | |
| int fifo_enable) | |
| { | |
| unsigned int line; | |
| const struct sram_channel *sram_ch = | |
| dev->channels[dev->_audio_upstream_channel].sram_channels; | |
| int offset = 0; | |
| /* scan lines */ | |
| for (line = 0; line < LINES_PER_AUDIO_BUFFER; line++) { | |
| *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); | |
| *(rp++) = cpu_to_le32(databuf_phys_addr + offset); | |
| *(rp++) = cpu_to_le32(0); /* bits 63-32 */ | |
| /* Check if we need to enable the FIFO | |
| * after the first 3 lines. | |
| * For the upstream audio channel, | |
| * the risc engine will enable the FIFO */ | |
| if (fifo_enable && line == 2) { | |
| *(rp++) = RISC_WRITECR; | |
| *(rp++) = sram_ch->dma_ctl; | |
| *(rp++) = sram_ch->fld_aud_fifo_en; | |
| *(rp++) = 0x00000020; | |
| } | |
| offset += AUDIO_LINE_SIZE; | |
| } | |
| return rp; | |
| } | |
| static int cx25821_risc_buffer_upstream_audio(struct cx25821_dev *dev, | |
| struct pci_dev *pci, | |
| unsigned int bpl, unsigned int lines) | |
| { | |
| __le32 *rp; | |
| int fifo_enable = 0; | |
| int frame = 0, i = 0; | |
| int frame_size = AUDIO_DATA_BUF_SZ; | |
| int databuf_offset = 0; | |
| int risc_flag = RISC_CNT_INC; | |
| dma_addr_t risc_phys_jump_addr; | |
| /* Virtual address of Risc buffer program */ | |
| rp = dev->_risc_virt_addr; | |
| /* sync instruction */ | |
| *(rp++) = cpu_to_le32(RISC_RESYNC | AUDIO_SYNC_LINE); | |
| for (frame = 0; frame < NUM_AUDIO_FRAMES; frame++) { | |
| databuf_offset = frame_size * frame; | |
| if (frame == 0) { | |
| fifo_enable = 1; | |
| risc_flag = RISC_CNT_RESET; | |
| } else { | |
| fifo_enable = 0; | |
| risc_flag = RISC_CNT_INC; | |
| } | |
| /* Calculate physical jump address */ | |
| if ((frame + 1) == NUM_AUDIO_FRAMES) { | |
| risc_phys_jump_addr = | |
| dev->_risc_phys_start_addr + | |
| RISC_SYNC_INSTRUCTION_SIZE; | |
| } else { | |
| risc_phys_jump_addr = | |
| dev->_risc_phys_start_addr + | |
| RISC_SYNC_INSTRUCTION_SIZE + | |
| AUDIO_RISC_DMA_BUF_SIZE * (frame + 1); | |
| } | |
| rp = cx25821_risc_field_upstream_audio(dev, rp, | |
| dev->_audiodata_buf_phys_addr + databuf_offset, | |
| bpl, fifo_enable); | |
| if (USE_RISC_NOOP_AUDIO) { | |
| for (i = 0; i < NUM_NO_OPS; i++) | |
| *(rp++) = cpu_to_le32(RISC_NOOP); | |
| } | |
| /* Loop to (Nth)FrameRISC or to Start of Risc program & | |
| * generate IRQ */ | |
| *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag); | |
| *(rp++) = cpu_to_le32(risc_phys_jump_addr); | |
| *(rp++) = cpu_to_le32(0); | |
| /* Recalculate virtual address based on frame index */ | |
| rp = dev->_risc_virt_addr + RISC_SYNC_INSTRUCTION_SIZE / 4 + | |
| (AUDIO_RISC_DMA_BUF_SIZE * (frame + 1) / 4); | |
| } | |
| return 0; | |
| } | |
| static void cx25821_free_memory_audio(struct cx25821_dev *dev) | |
| { | |
| if (dev->_risc_virt_addr) { | |
| pci_free_consistent(dev->pci, dev->_audiorisc_size, | |
| dev->_risc_virt_addr, dev->_risc_phys_addr); | |
| dev->_risc_virt_addr = NULL; | |
| } | |
| if (dev->_audiodata_buf_virt_addr) { | |
| pci_free_consistent(dev->pci, dev->_audiodata_buf_size, | |
| dev->_audiodata_buf_virt_addr, | |
| dev->_audiodata_buf_phys_addr); | |
| dev->_audiodata_buf_virt_addr = NULL; | |
| } | |
| } | |
| void cx25821_stop_upstream_audio(struct cx25821_dev *dev) | |
| { | |
| const struct sram_channel *sram_ch = | |
| dev->channels[AUDIO_UPSTREAM_SRAM_CHANNEL_B].sram_channels; | |
| u32 tmp = 0; | |
| if (!dev->_audio_is_running) { | |
| printk(KERN_DEBUG | |
| pr_fmt("No audio file is currently running so return!\n")); | |
| return; | |
| } | |
| /* Disable RISC interrupts */ | |
| cx_write(sram_ch->int_msk, 0); | |
| /* Turn OFF risc and fifo enable in AUD_DMA_CNTRL */ | |
| tmp = cx_read(sram_ch->dma_ctl); | |
| cx_write(sram_ch->dma_ctl, | |
| tmp & ~(sram_ch->fld_aud_fifo_en | sram_ch->fld_aud_risc_en)); | |
| /* Clear data buffer memory */ | |
| if (dev->_audiodata_buf_virt_addr) | |
| memset(dev->_audiodata_buf_virt_addr, 0, | |
| dev->_audiodata_buf_size); | |
| dev->_audio_is_running = 0; | |
| dev->_is_first_audio_frame = 0; | |
| dev->_audioframe_count = 0; | |
| dev->_audiofile_status = END_OF_FILE; | |
| flush_work(&dev->_audio_work_entry); | |
| kfree(dev->_audiofilename); | |
| } | |
| void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev) | |
| { | |
| if (dev->_audio_is_running) | |
| cx25821_stop_upstream_audio(dev); | |
| cx25821_free_memory_audio(dev); | |
| } | |
| static int cx25821_get_audio_data(struct cx25821_dev *dev, | |
| const struct sram_channel *sram_ch) | |
| { | |
| struct file *file; | |
| int frame_index_temp = dev->_audioframe_index; | |
| int i = 0; | |
| int frame_size = AUDIO_DATA_BUF_SZ; | |
| int frame_offset = frame_size * frame_index_temp; | |
| char mybuf[AUDIO_LINE_SIZE]; | |
| loff_t file_offset = dev->_audioframe_count * frame_size; | |
| char *p = NULL; | |
| if (dev->_audiofile_status == END_OF_FILE) | |
| return 0; | |
| file = filp_open(dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0); | |
| if (IS_ERR(file)) { | |
| pr_err("%s(): ERROR opening file(%s) with errno = %ld!\n", | |
| __func__, dev->_audiofilename, -PTR_ERR(file)); | |
| return PTR_ERR(file); | |
| } | |
| if (dev->_audiodata_buf_virt_addr) | |
| p = (char *)dev->_audiodata_buf_virt_addr + frame_offset; | |
| for (i = 0; i < dev->_audio_lines_count; i++) { | |
| int n = kernel_read(file, file_offset, mybuf, AUDIO_LINE_SIZE); | |
| if (n < AUDIO_LINE_SIZE) { | |
| pr_info("Done: exit %s() since no more bytes to read from Audio file\n", | |
| __func__); | |
| dev->_audiofile_status = END_OF_FILE; | |
| fput(file); | |
| return 0; | |
| } | |
| dev->_audiofile_status = IN_PROGRESS; | |
| if (p) { | |
| memcpy(p, mybuf, n); | |
| p += n; | |
| } | |
| file_offset += n; | |
| } | |
| dev->_audioframe_count++; | |
| fput(file); | |
| return 0; | |
| } | |
| static void cx25821_audioups_handler(struct work_struct *work) | |
| { | |
| struct cx25821_dev *dev = container_of(work, struct cx25821_dev, | |
| _audio_work_entry); | |
| if (!dev) { | |
| pr_err("ERROR %s(): since container_of(work_struct) FAILED!\n", | |
| __func__); | |
| return; | |
| } | |
| cx25821_get_audio_data(dev, dev->channels[dev->_audio_upstream_channel]. | |
| sram_channels); | |
| } | |
| static int cx25821_openfile_audio(struct cx25821_dev *dev, | |
| const struct sram_channel *sram_ch) | |
| { | |
| char *p = (void *)dev->_audiodata_buf_virt_addr; | |
| struct file *file; | |
| loff_t offset; | |
| int i, j; | |
| file = filp_open(dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0); | |
| if (IS_ERR(file)) { | |
| pr_err("%s(): ERROR opening file(%s) with errno = %ld!\n", | |
| __func__, dev->_audiofilename, PTR_ERR(file)); | |
| return PTR_ERR(file); | |
| } | |
| for (j = 0, offset = 0; j < NUM_AUDIO_FRAMES; j++) { | |
| for (i = 0; i < dev->_audio_lines_count; i++) { | |
| char buf[AUDIO_LINE_SIZE]; | |
| int n = kernel_read(file, offset, buf, | |
| AUDIO_LINE_SIZE); | |
| if (n < AUDIO_LINE_SIZE) { | |
| pr_info("Done: exit %s() since no more bytes to read from Audio file\n", | |
| __func__); | |
| dev->_audiofile_status = END_OF_FILE; | |
| fput(file); | |
| return 0; | |
| } | |
| if (p) | |
| memcpy(p + offset, buf, n); | |
| offset += n; | |
| } | |
| dev->_audioframe_count++; | |
| } | |
| dev->_audiofile_status = IN_PROGRESS; | |
| fput(file); | |
| return 0; | |
| } | |
| static int cx25821_audio_upstream_buffer_prepare(struct cx25821_dev *dev, | |
| const struct sram_channel *sram_ch, | |
| int bpl) | |
| { | |
| int ret = 0; | |
| dma_addr_t dma_addr; | |
| dma_addr_t data_dma_addr; | |
| cx25821_free_memory_audio(dev); | |
| dev->_risc_virt_addr = pci_alloc_consistent(dev->pci, | |
| dev->audio_upstream_riscbuf_size, &dma_addr); | |
| dev->_risc_virt_start_addr = dev->_risc_virt_addr; | |
| dev->_risc_phys_start_addr = dma_addr; | |
| dev->_risc_phys_addr = dma_addr; | |
| dev->_audiorisc_size = dev->audio_upstream_riscbuf_size; | |
| if (!dev->_risc_virt_addr) { | |
| printk(KERN_DEBUG | |
| pr_fmt("ERROR: pci_alloc_consistent() FAILED to allocate memory for RISC program! Returning\n")); | |
| return -ENOMEM; | |
| } | |
| /* Clear out memory at address */ | |
| memset(dev->_risc_virt_addr, 0, dev->_audiorisc_size); | |
| /* For Audio Data buffer allocation */ | |
| dev->_audiodata_buf_virt_addr = pci_alloc_consistent(dev->pci, | |
| dev->audio_upstream_databuf_size, &data_dma_addr); | |
| dev->_audiodata_buf_phys_addr = data_dma_addr; | |
| dev->_audiodata_buf_size = dev->audio_upstream_databuf_size; | |
| if (!dev->_audiodata_buf_virt_addr) { | |
| printk(KERN_DEBUG | |
| pr_fmt("ERROR: pci_alloc_consistent() FAILED to allocate memory for data buffer! Returning\n")); | |
| return -ENOMEM; | |
| } | |
| /* Clear out memory at address */ | |
| memset(dev->_audiodata_buf_virt_addr, 0, dev->_audiodata_buf_size); | |
| ret = cx25821_openfile_audio(dev, sram_ch); | |
| if (ret < 0) | |
| return ret; | |
| /* Creating RISC programs */ | |
| ret = cx25821_risc_buffer_upstream_audio(dev, dev->pci, bpl, | |
| dev->_audio_lines_count); | |
| if (ret < 0) { | |
| printk(KERN_DEBUG | |
| pr_fmt("ERROR creating audio upstream RISC programs!\n")); | |
| goto error; | |
| } | |
| return 0; | |
| error: | |
| return ret; | |
| } | |
| static int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, | |
| u32 status) | |
| { | |
| int i = 0; | |
| u32 int_msk_tmp; | |
| const struct sram_channel *channel = dev->channels[chan_num].sram_channels; | |
| dma_addr_t risc_phys_jump_addr; | |
| __le32 *rp; | |
| if (status & FLD_AUD_SRC_RISCI1) { | |
| /* Get interrupt_index of the program that interrupted */ | |
| u32 prog_cnt = cx_read(channel->gpcnt); | |
| /* Since we've identified our IRQ, clear our bits from the | |
| * interrupt mask and interrupt status registers */ | |
| cx_write(channel->int_msk, 0); | |
| cx_write(channel->int_stat, cx_read(channel->int_stat)); | |
| spin_lock(&dev->slock); | |
| while (prog_cnt != dev->_last_index_irq) { | |
| /* Update _last_index_irq */ | |
| if (dev->_last_index_irq < (NUMBER_OF_PROGRAMS - 1)) | |
| dev->_last_index_irq++; | |
| else | |
| dev->_last_index_irq = 0; | |
| dev->_audioframe_index = dev->_last_index_irq; | |
| schedule_work(&dev->_audio_work_entry); | |
| } | |
| if (dev->_is_first_audio_frame) { | |
| dev->_is_first_audio_frame = 0; | |
| if (dev->_risc_virt_start_addr != NULL) { | |
| risc_phys_jump_addr = | |
| dev->_risc_phys_start_addr + | |
| RISC_SYNC_INSTRUCTION_SIZE + | |
| AUDIO_RISC_DMA_BUF_SIZE; | |
| rp = cx25821_risc_field_upstream_audio(dev, | |
| dev->_risc_virt_start_addr + 1, | |
| dev->_audiodata_buf_phys_addr, | |
| AUDIO_LINE_SIZE, FIFO_DISABLE); | |
| if (USE_RISC_NOOP_AUDIO) { | |
| for (i = 0; i < NUM_NO_OPS; i++) { | |
| *(rp++) = | |
| cpu_to_le32(RISC_NOOP); | |
| } | |
| } | |
| /* Jump to 2nd Audio Frame */ | |
| *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | | |
| RISC_CNT_RESET); | |
| *(rp++) = cpu_to_le32(risc_phys_jump_addr); | |
| *(rp++) = cpu_to_le32(0); | |
| } | |
| } | |
| spin_unlock(&dev->slock); | |
| } else { | |
| if (status & FLD_AUD_SRC_OF) | |
| pr_warn("%s(): Audio Received Overflow Error Interrupt!\n", | |
| __func__); | |
| if (status & FLD_AUD_SRC_SYNC) | |
| pr_warn("%s(): Audio Received Sync Error Interrupt!\n", | |
| __func__); | |
| if (status & FLD_AUD_SRC_OPC_ERR) | |
| pr_warn("%s(): Audio Received OpCode Error Interrupt!\n", | |
| __func__); | |
| /* Read and write back the interrupt status register to clear | |
| * our bits */ | |
| cx_write(channel->int_stat, cx_read(channel->int_stat)); | |
| } | |
| if (dev->_audiofile_status == END_OF_FILE) { | |
| pr_warn("EOF Channel Audio Framecount = %d\n", | |
| dev->_audioframe_count); | |
| return -1; | |
| } | |
| /* ElSE, set the interrupt mask register, re-enable irq. */ | |
| int_msk_tmp = cx_read(channel->int_msk); | |
| cx_write(channel->int_msk, int_msk_tmp |= _intr_msk); | |
| return 0; | |
| } | |
| static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id) | |
| { | |
| struct cx25821_dev *dev = dev_id; | |
| u32 audio_status; | |
| int handled = 0; | |
| const struct sram_channel *sram_ch; | |
| if (!dev) | |
| return -1; | |
| sram_ch = dev->channels[dev->_audio_upstream_channel].sram_channels; | |
| audio_status = cx_read(sram_ch->int_stat); | |
| /* Only deal with our interrupt */ | |
| if (audio_status) { | |
| handled = cx25821_audio_upstream_irq(dev, | |
| dev->_audio_upstream_channel, audio_status); | |
| } | |
| if (handled < 0) | |
| cx25821_stop_upstream_audio(dev); | |
| else | |
| handled += handled; | |
| return IRQ_RETVAL(handled); | |
| } | |
| static void cx25821_wait_fifo_enable(struct cx25821_dev *dev, | |
| const struct sram_channel *sram_ch) | |
| { | |
| int count = 0; | |
| u32 tmp; | |
| do { | |
| /* Wait 10 microsecond before checking to see if the FIFO is | |
| * turned ON. */ | |
| udelay(10); | |
| tmp = cx_read(sram_ch->dma_ctl); | |
| /* 10 millisecond timeout */ | |
| if (count++ > 1000) { | |
| pr_err("ERROR: %s() fifo is NOT turned on. Timeout!\n", | |
| __func__); | |
| return; | |
| } | |
| } while (!(tmp & sram_ch->fld_aud_fifo_en)); | |
| } | |
| static int cx25821_start_audio_dma_upstream(struct cx25821_dev *dev, | |
| const struct sram_channel *sram_ch) | |
| { | |
| u32 tmp = 0; | |
| int err = 0; | |
| /* Set the physical start address of the RISC program in the initial | |
| * program counter(IPC) member of the CMDS. */ | |
| cx_write(sram_ch->cmds_start + 0, dev->_risc_phys_addr); | |
| /* Risc IPC High 64 bits 63-32 */ | |
| cx_write(sram_ch->cmds_start + 4, 0); | |
| /* reset counter */ | |
| cx_write(sram_ch->gpcnt_ctl, 3); | |
| /* Set the line length (It looks like we do not need to set the | |
| * line length) */ | |
| cx_write(sram_ch->aud_length, AUDIO_LINE_SIZE & FLD_AUD_DST_LN_LNGTH); | |
| /* Set the input mode to 16-bit */ | |
| tmp = cx_read(sram_ch->aud_cfg); | |
| tmp |= FLD_AUD_SRC_ENABLE | FLD_AUD_DST_PK_MODE | FLD_AUD_CLK_ENABLE | | |
| FLD_AUD_MASTER_MODE | FLD_AUD_CLK_SELECT_PLL_D | | |
| FLD_AUD_SONY_MODE; | |
| cx_write(sram_ch->aud_cfg, tmp); | |
| /* Read and write back the interrupt status register to clear it */ | |
| tmp = cx_read(sram_ch->int_stat); | |
| cx_write(sram_ch->int_stat, tmp); | |
| /* Clear our bits from the interrupt status register. */ | |
| cx_write(sram_ch->int_stat, _intr_msk); | |
| /* Set the interrupt mask register, enable irq. */ | |
| cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit)); | |
| tmp = cx_read(sram_ch->int_msk); | |
| cx_write(sram_ch->int_msk, tmp |= _intr_msk); | |
| err = request_irq(dev->pci->irq, cx25821_upstream_irq_audio, | |
| IRQF_SHARED, dev->name, dev); | |
| if (err < 0) { | |
| pr_err("%s: can't get upstream IRQ %d\n", dev->name, | |
| dev->pci->irq); | |
| goto fail_irq; | |
| } | |
| /* Start the DMA engine */ | |
| tmp = cx_read(sram_ch->dma_ctl); | |
| cx_set(sram_ch->dma_ctl, tmp | sram_ch->fld_aud_risc_en); | |
| dev->_audio_is_running = 1; | |
| dev->_is_first_audio_frame = 1; | |
| /* The fifo_en bit turns on by the first Risc program */ | |
| cx25821_wait_fifo_enable(dev, sram_ch); | |
| return 0; | |
| fail_irq: | |
| cx25821_dev_unregister(dev); | |
| return err; | |
| } | |
| int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select) | |
| { | |
| const struct sram_channel *sram_ch; | |
| int err = 0; | |
| if (dev->_audio_is_running) { | |
| pr_warn("Audio Channel is still running so return!\n"); | |
| return 0; | |
| } | |
| dev->_audio_upstream_channel = channel_select; | |
| sram_ch = dev->channels[channel_select].sram_channels; | |
| /* Work queue */ | |
| INIT_WORK(&dev->_audio_work_entry, cx25821_audioups_handler); | |
| dev->_last_index_irq = 0; | |
| dev->_audio_is_running = 0; | |
| dev->_audioframe_count = 0; | |
| dev->_audiofile_status = RESET_STATUS; | |
| dev->_audio_lines_count = LINES_PER_AUDIO_BUFFER; | |
| _line_size = AUDIO_LINE_SIZE; | |
| if ((dev->input_audiofilename) && | |
| (strcmp(dev->input_audiofilename, "") != 0)) | |
| dev->_audiofilename = kstrdup(dev->input_audiofilename, | |
| GFP_KERNEL); | |
| else | |
| dev->_audiofilename = kstrdup(_defaultAudioName, | |
| GFP_KERNEL); | |
| if (!dev->_audiofilename) { | |
| err = -ENOMEM; | |
| goto error; | |
| } | |
| cx25821_sram_channel_setup_upstream_audio(dev, sram_ch, | |
| _line_size, 0); | |
| dev->audio_upstream_riscbuf_size = | |
| AUDIO_RISC_DMA_BUF_SIZE * NUM_AUDIO_PROGS + | |
| RISC_SYNC_INSTRUCTION_SIZE; | |
| dev->audio_upstream_databuf_size = AUDIO_DATA_BUF_SZ * NUM_AUDIO_PROGS; | |
| /* Allocating buffers and prepare RISC program */ | |
| err = cx25821_audio_upstream_buffer_prepare(dev, sram_ch, | |
| _line_size); | |
| if (err < 0) { | |
| pr_err("%s: Failed to set up Audio upstream buffers!\n", | |
| dev->name); | |
| goto error; | |
| } | |
| /* Start RISC engine */ | |
| cx25821_start_audio_dma_upstream(dev, sram_ch); | |
| return 0; | |
| error: | |
| cx25821_dev_unregister(dev); | |
| return err; | |
| } |