| /* |
| * 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; |
| |
| int cx25821_sram_channel_setup_upstream_audio(struct cx25821_dev *dev, |
| 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; |
| struct sram_channel *sram_ch = |
| dev->channels[dev->_audio_upstream_channel_select].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; |
| } |
| |
| 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; |
| } |
| |
| 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) |
| { |
| 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; |
| |
| if (dev->_irq_audio_queues) { |
| kfree(dev->_irq_audio_queues); |
| dev->_irq_audio_queues = NULL; |
| } |
| |
| if (dev->_audiofilename != NULL) |
| 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); |
| } |
| |
| int cx25821_get_audio_data(struct cx25821_dev *dev, |
| struct sram_channel *sram_ch) |
| { |
| struct file *myfile; |
| int frame_index_temp = dev->_audioframe_index; |
| int i = 0; |
| int line_size = AUDIO_LINE_SIZE; |
| int frame_size = AUDIO_DATA_BUF_SZ; |
| int frame_offset = frame_size * frame_index_temp; |
| ssize_t vfs_read_retval = 0; |
| char mybuf[line_size]; |
| loff_t file_offset = dev->_audioframe_count * frame_size; |
| loff_t pos; |
| mm_segment_t old_fs; |
| |
| if (dev->_audiofile_status == END_OF_FILE) |
| return 0; |
| |
| myfile = filp_open(dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0); |
| |
| if (IS_ERR(myfile)) { |
| const int open_errno = -PTR_ERR(myfile); |
| pr_err("%s(): ERROR opening file(%s) with errno = %d!\n", |
| __func__, dev->_audiofilename, open_errno); |
| return PTR_ERR(myfile); |
| } else { |
| if (!(myfile->f_op)) { |
| pr_err("%s(): File has no file operations registered!\n", |
| __func__); |
| filp_close(myfile, NULL); |
| return -EIO; |
| } |
| |
| if (!myfile->f_op->read) { |
| pr_err("%s(): File has no READ operations registered!\n", |
| __func__); |
| filp_close(myfile, NULL); |
| return -EIO; |
| } |
| |
| pos = myfile->f_pos; |
| old_fs = get_fs(); |
| set_fs(KERNEL_DS); |
| |
| for (i = 0; i < dev->_audio_lines_count; i++) { |
| pos = file_offset; |
| |
| vfs_read_retval = |
| vfs_read(myfile, mybuf, line_size, &pos); |
| |
| if (vfs_read_retval > 0 && vfs_read_retval == line_size |
| && dev->_audiodata_buf_virt_addr != NULL) { |
| memcpy((void *)(dev->_audiodata_buf_virt_addr + |
| frame_offset / 4), mybuf, |
| vfs_read_retval); |
| } |
| |
| file_offset += vfs_read_retval; |
| frame_offset += vfs_read_retval; |
| |
| if (vfs_read_retval < line_size) { |
| pr_info("Done: exit %s() since no more bytes to read from Audio file\n", |
| __func__); |
| break; |
| } |
| } |
| |
| if (i > 0) |
| dev->_audioframe_count++; |
| |
| dev->_audiofile_status = |
| (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE; |
| |
| set_fs(old_fs); |
| filp_close(myfile, NULL); |
| } |
| |
| 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_select]. |
| sram_channels); |
| } |
| |
| int cx25821_openfile_audio(struct cx25821_dev *dev, |
| struct sram_channel *sram_ch) |
| { |
| struct file *myfile; |
| int i = 0, j = 0; |
| int line_size = AUDIO_LINE_SIZE; |
| ssize_t vfs_read_retval = 0; |
| char mybuf[line_size]; |
| loff_t pos; |
| loff_t offset = (unsigned long)0; |
| mm_segment_t old_fs; |
| |
| myfile = filp_open(dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0); |
| |
| if (IS_ERR(myfile)) { |
| const int open_errno = -PTR_ERR(myfile); |
| pr_err("%s(): ERROR opening file(%s) with errno = %d!\n", |
| __func__, dev->_audiofilename, open_errno); |
| return PTR_ERR(myfile); |
| } else { |
| if (!(myfile->f_op)) { |
| pr_err("%s(): File has no file operations registered!\n", |
| __func__); |
| filp_close(myfile, NULL); |
| return -EIO; |
| } |
| |
| if (!myfile->f_op->read) { |
| pr_err("%s(): File has no READ operations registered!\n", |
| __func__); |
| filp_close(myfile, NULL); |
| return -EIO; |
| } |
| |
| pos = myfile->f_pos; |
| old_fs = get_fs(); |
| set_fs(KERNEL_DS); |
| |
| for (j = 0; j < NUM_AUDIO_FRAMES; j++) { |
| for (i = 0; i < dev->_audio_lines_count; i++) { |
| pos = offset; |
| |
| vfs_read_retval = |
| vfs_read(myfile, mybuf, line_size, &pos); |
| |
| if (vfs_read_retval > 0 |
| && vfs_read_retval == line_size |
| && dev->_audiodata_buf_virt_addr != NULL) { |
| memcpy((void *)(dev-> |
| _audiodata_buf_virt_addr |
| + offset / 4), mybuf, |
| vfs_read_retval); |
| } |
| |
| offset += vfs_read_retval; |
| |
| if (vfs_read_retval < line_size) { |
| pr_info("Done: exit %s() since no more bytes to read from Audio file\n", |
| __func__); |
| break; |
| } |
| } |
| |
| if (i > 0) |
| dev->_audioframe_count++; |
| |
| if (vfs_read_retval < line_size) |
| break; |
| } |
| |
| dev->_audiofile_status = |
| (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE; |
| |
| set_fs(old_fs); |
| myfile->f_pos = 0; |
| filp_close(myfile, NULL); |
| } |
| |
| return 0; |
| } |
| |
| static int cx25821_audio_upstream_buffer_prepare(struct cx25821_dev *dev, |
| 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; |
| } |
| |
| int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, |
| u32 status) |
| { |
| int i = 0; |
| u32 int_msk_tmp; |
| 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; |
| |
| queue_work(dev->_irq_audio_queues, |
| &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 msk_stat, audio_status; |
| int handled = 0; |
| struct sram_channel *sram_ch; |
| |
| if (!dev) |
| return -1; |
| |
| sram_ch = dev->channels[dev->_audio_upstream_channel_select].sram_channels; |
| |
| msk_stat = cx_read(sram_ch->int_mstat); |
| 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_select, |
| 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, |
| 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)); |
| |
| } |
| |
| int cx25821_start_audio_dma_upstream(struct cx25821_dev *dev, |
| 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 | IRQF_DISABLED, 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) |
| { |
| struct sram_channel *sram_ch; |
| int retval = 0; |
| int err = 0; |
| int str_length = 0; |
| |
| if (dev->_audio_is_running) { |
| pr_warn("Audio Channel is still running so return!\n"); |
| return 0; |
| } |
| |
| dev->_audio_upstream_channel_select = channel_select; |
| sram_ch = dev->channels[channel_select].sram_channels; |
| |
| /* Work queue */ |
| INIT_WORK(&dev->_audio_work_entry, cx25821_audioups_handler); |
| dev->_irq_audio_queues = |
| create_singlethread_workqueue("cx25821_audioworkqueue"); |
| |
| if (!dev->_irq_audio_queues) { |
| printk(KERN_DEBUG |
| pr_fmt("ERROR: create_singlethread_workqueue() for Audio FAILED!\n")); |
| return -ENOMEM; |
| } |
| |
| 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) { |
| str_length = strlen(dev->input_audiofilename); |
| dev->_audiofilename = kmalloc(str_length + 1, GFP_KERNEL); |
| |
| if (!dev->_audiofilename) |
| goto error; |
| |
| memcpy(dev->_audiofilename, dev->input_audiofilename, |
| str_length + 1); |
| |
| /* Default if filename is empty string */ |
| if (strcmp(dev->input_audiofilename, "") == 0) |
| dev->_audiofilename = "/root/audioGOOD.wav"; |
| } else { |
| str_length = strlen(_defaultAudioName); |
| dev->_audiofilename = kmalloc(str_length + 1, GFP_KERNEL); |
| |
| if (!dev->_audiofilename) |
| goto error; |
| |
| memcpy(dev->_audiofilename, _defaultAudioName, str_length + 1); |
| } |
| |
| retval = |
| 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 */ |
| retval = |
| cx25821_audio_upstream_buffer_prepare(dev, sram_ch, _line_size); |
| if (retval < 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; |
| } |