18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015 VanguardiaSur - www.vanguardiasur.com.ar 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Based on the audio support from the tw6869 driver: 68c2ecf20Sopenharmony_ci * Copyright 2015 www.starterkit.ru <info@starterkit.ru> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on: 98c2ecf20Sopenharmony_ci * Driver for Intersil|Techwell TW6869 based DVR cards 108c2ecf20Sopenharmony_ci * (c) 2011-12 liran <jli11@intersil.com> [Intersil|Techwell China] 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/types.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/kmod.h> 188c2ecf20Sopenharmony_ci#include <linux/mutex.h> 198c2ecf20Sopenharmony_ci#include <linux/pci.h> 208c2ecf20Sopenharmony_ci#include <linux/delay.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <sound/core.h> 238c2ecf20Sopenharmony_ci#include <sound/initval.h> 248c2ecf20Sopenharmony_ci#include <sound/pcm.h> 258c2ecf20Sopenharmony_ci#include <sound/control.h> 268c2ecf20Sopenharmony_ci#include "tw686x.h" 278c2ecf20Sopenharmony_ci#include "tw686x-regs.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define AUDIO_CHANNEL_OFFSET 8 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_civoid tw686x_audio_irq(struct tw686x_dev *dev, unsigned long requests, 328c2ecf20Sopenharmony_ci unsigned int pb_status) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci unsigned long flags; 358c2ecf20Sopenharmony_ci unsigned int ch, pb; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci for_each_set_bit(ch, &requests, max_channels(dev)) { 388c2ecf20Sopenharmony_ci struct tw686x_audio_channel *ac = &dev->audio_channels[ch]; 398c2ecf20Sopenharmony_ci struct tw686x_audio_buf *done = NULL; 408c2ecf20Sopenharmony_ci struct tw686x_audio_buf *next = NULL; 418c2ecf20Sopenharmony_ci struct tw686x_dma_desc *desc; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci pb = !!(pb_status & BIT(AUDIO_CHANNEL_OFFSET + ch)); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci spin_lock_irqsave(&ac->lock, flags); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci /* Sanity check */ 488c2ecf20Sopenharmony_ci if (!ac->ss || !ac->curr_bufs[0] || !ac->curr_bufs[1]) { 498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 508c2ecf20Sopenharmony_ci continue; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (!list_empty(&ac->buf_list)) { 548c2ecf20Sopenharmony_ci next = list_first_entry(&ac->buf_list, 558c2ecf20Sopenharmony_ci struct tw686x_audio_buf, list); 568c2ecf20Sopenharmony_ci list_move_tail(&next->list, &ac->buf_list); 578c2ecf20Sopenharmony_ci done = ac->curr_bufs[!pb]; 588c2ecf20Sopenharmony_ci ac->curr_bufs[pb] = next; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (!done || !next) 638c2ecf20Sopenharmony_ci continue; 648c2ecf20Sopenharmony_ci /* 658c2ecf20Sopenharmony_ci * Checking for a non-nil dma_desc[pb]->virt buffer is 668c2ecf20Sopenharmony_ci * the same as checking for memcpy DMA mode. 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_ci desc = &ac->dma_descs[pb]; 698c2ecf20Sopenharmony_ci if (desc->virt) { 708c2ecf20Sopenharmony_ci memcpy(done->virt, desc->virt, 718c2ecf20Sopenharmony_ci dev->period_size); 728c2ecf20Sopenharmony_ci } else { 738c2ecf20Sopenharmony_ci u32 reg = pb ? ADMA_B_ADDR[ch] : ADMA_P_ADDR[ch]; 748c2ecf20Sopenharmony_ci reg_write(dev, reg, next->dma); 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci ac->ptr = done->dma - ac->buf[0].dma; 778c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(ac->ss); 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* 828c2ecf20Sopenharmony_ci * Audio parameters are global and shared among all 838c2ecf20Sopenharmony_ci * capture channels. The driver prevents changes to 848c2ecf20Sopenharmony_ci * the parameters if any audio channel is capturing. 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware tw686x_capture_hw = { 878c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | 888c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 898c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 908c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 918c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 928c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 938c2ecf20Sopenharmony_ci .rate_min = 8000, 948c2ecf20Sopenharmony_ci .rate_max = 48000, 958c2ecf20Sopenharmony_ci .channels_min = 1, 968c2ecf20Sopenharmony_ci .channels_max = 1, 978c2ecf20Sopenharmony_ci .buffer_bytes_max = TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX, 988c2ecf20Sopenharmony_ci .period_bytes_min = AUDIO_DMA_SIZE_MIN, 998c2ecf20Sopenharmony_ci .period_bytes_max = AUDIO_DMA_SIZE_MAX, 1008c2ecf20Sopenharmony_ci .periods_min = TW686X_AUDIO_PERIODS_MIN, 1018c2ecf20Sopenharmony_ci .periods_max = TW686X_AUDIO_PERIODS_MAX, 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int tw686x_pcm_open(struct snd_pcm_substream *ss) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct tw686x_dev *dev = snd_pcm_substream_chip(ss); 1078c2ecf20Sopenharmony_ci struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; 1088c2ecf20Sopenharmony_ci struct snd_pcm_runtime *rt = ss->runtime; 1098c2ecf20Sopenharmony_ci int err; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci ac->ss = ss; 1128c2ecf20Sopenharmony_ci rt->hw = tw686x_capture_hw; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); 1158c2ecf20Sopenharmony_ci if (err < 0) 1168c2ecf20Sopenharmony_ci return err; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return 0; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int tw686x_pcm_close(struct snd_pcm_substream *ss) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct tw686x_dev *dev = snd_pcm_substream_chip(ss); 1248c2ecf20Sopenharmony_ci struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci ac->ss = NULL; 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int tw686x_pcm_prepare(struct snd_pcm_substream *ss) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct tw686x_dev *dev = snd_pcm_substream_chip(ss); 1338c2ecf20Sopenharmony_ci struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; 1348c2ecf20Sopenharmony_ci struct snd_pcm_runtime *rt = ss->runtime; 1358c2ecf20Sopenharmony_ci unsigned int period_size = snd_pcm_lib_period_bytes(ss); 1368c2ecf20Sopenharmony_ci struct tw686x_audio_buf *p_buf, *b_buf; 1378c2ecf20Sopenharmony_ci unsigned long flags; 1388c2ecf20Sopenharmony_ci int i; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 1418c2ecf20Sopenharmony_ci /* 1428c2ecf20Sopenharmony_ci * Given the audio parameters are global (i.e. shared across 1438c2ecf20Sopenharmony_ci * DMA channels), we need to check new params are allowed. 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci if (((dev->audio_rate != rt->rate) || 1468c2ecf20Sopenharmony_ci (dev->period_size != period_size)) && dev->audio_enabled) 1478c2ecf20Sopenharmony_ci goto err_audio_busy; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch); 1508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (dev->audio_rate != rt->rate) { 1538c2ecf20Sopenharmony_ci u32 reg; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci dev->audio_rate = rt->rate; 1568c2ecf20Sopenharmony_ci reg = ((125000000 / rt->rate) << 16) + 1578c2ecf20Sopenharmony_ci ((125000000 % rt->rate) << 16) / rt->rate; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci reg_write(dev, AUDIO_CONTROL2, reg); 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (dev->period_size != period_size) { 1638c2ecf20Sopenharmony_ci u32 reg; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci dev->period_size = period_size; 1668c2ecf20Sopenharmony_ci reg = reg_read(dev, AUDIO_CONTROL1); 1678c2ecf20Sopenharmony_ci reg &= ~(AUDIO_DMA_SIZE_MASK << AUDIO_DMA_SIZE_SHIFT); 1688c2ecf20Sopenharmony_ci reg |= period_size << AUDIO_DMA_SIZE_SHIFT; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci reg_write(dev, AUDIO_CONTROL1, reg); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (rt->periods < TW686X_AUDIO_PERIODS_MIN || 1748c2ecf20Sopenharmony_ci rt->periods > TW686X_AUDIO_PERIODS_MAX) 1758c2ecf20Sopenharmony_ci return -EINVAL; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci spin_lock_irqsave(&ac->lock, flags); 1788c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ac->buf_list); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci for (i = 0; i < rt->periods; i++) { 1818c2ecf20Sopenharmony_ci ac->buf[i].dma = rt->dma_addr + period_size * i; 1828c2ecf20Sopenharmony_ci ac->buf[i].virt = rt->dma_area + period_size * i; 1838c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ac->buf[i].list); 1848c2ecf20Sopenharmony_ci list_add_tail(&ac->buf[i].list, &ac->buf_list); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci p_buf = list_first_entry(&ac->buf_list, struct tw686x_audio_buf, list); 1888c2ecf20Sopenharmony_ci list_move_tail(&p_buf->list, &ac->buf_list); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci b_buf = list_first_entry(&ac->buf_list, struct tw686x_audio_buf, list); 1918c2ecf20Sopenharmony_ci list_move_tail(&b_buf->list, &ac->buf_list); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci ac->curr_bufs[0] = p_buf; 1948c2ecf20Sopenharmony_ci ac->curr_bufs[1] = b_buf; 1958c2ecf20Sopenharmony_ci ac->ptr = 0; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (dev->dma_mode != TW686X_DMA_MODE_MEMCPY) { 1988c2ecf20Sopenharmony_ci reg_write(dev, ADMA_P_ADDR[ac->ch], p_buf->dma); 1998c2ecf20Sopenharmony_ci reg_write(dev, ADMA_B_ADDR[ac->ch], b_buf->dma); 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cierr_audio_busy: 2078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 2088c2ecf20Sopenharmony_ci return -EBUSY; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int tw686x_pcm_trigger(struct snd_pcm_substream *ss, int cmd) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct tw686x_dev *dev = snd_pcm_substream_chip(ss); 2148c2ecf20Sopenharmony_ci struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; 2158c2ecf20Sopenharmony_ci unsigned long flags; 2168c2ecf20Sopenharmony_ci int err = 0; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci switch (cmd) { 2198c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 2208c2ecf20Sopenharmony_ci if (ac->curr_bufs[0] && ac->curr_bufs[1]) { 2218c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 2228c2ecf20Sopenharmony_ci dev->audio_enabled = 1; 2238c2ecf20Sopenharmony_ci tw686x_enable_channel(dev, 2248c2ecf20Sopenharmony_ci AUDIO_CHANNEL_OFFSET + ac->ch); 2258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci mod_timer(&dev->dma_delay_timer, 2288c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(100)); 2298c2ecf20Sopenharmony_ci } else { 2308c2ecf20Sopenharmony_ci err = -EIO; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci break; 2338c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 2348c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 2358c2ecf20Sopenharmony_ci dev->audio_enabled = 0; 2368c2ecf20Sopenharmony_ci tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch); 2378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci spin_lock_irqsave(&ac->lock, flags); 2408c2ecf20Sopenharmony_ci ac->curr_bufs[0] = NULL; 2418c2ecf20Sopenharmony_ci ac->curr_bufs[1] = NULL; 2428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 2438c2ecf20Sopenharmony_ci break; 2448c2ecf20Sopenharmony_ci default: 2458c2ecf20Sopenharmony_ci err = -EINVAL; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci return err; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t tw686x_pcm_pointer(struct snd_pcm_substream *ss) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct tw686x_dev *dev = snd_pcm_substream_chip(ss); 2538c2ecf20Sopenharmony_ci struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return bytes_to_frames(ss->runtime, ac->ptr); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops tw686x_pcm_ops = { 2598c2ecf20Sopenharmony_ci .open = tw686x_pcm_open, 2608c2ecf20Sopenharmony_ci .close = tw686x_pcm_close, 2618c2ecf20Sopenharmony_ci .prepare = tw686x_pcm_prepare, 2628c2ecf20Sopenharmony_ci .trigger = tw686x_pcm_trigger, 2638c2ecf20Sopenharmony_ci .pointer = tw686x_pcm_pointer, 2648c2ecf20Sopenharmony_ci}; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic int tw686x_snd_pcm_init(struct tw686x_dev *dev) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct snd_card *card = dev->snd_card; 2698c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 2708c2ecf20Sopenharmony_ci struct snd_pcm_substream *ss; 2718c2ecf20Sopenharmony_ci unsigned int i; 2728c2ecf20Sopenharmony_ci int err; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci err = snd_pcm_new(card, card->driver, 0, 0, max_channels(dev), &pcm); 2758c2ecf20Sopenharmony_ci if (err < 0) 2768c2ecf20Sopenharmony_ci return err; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &tw686x_pcm_ops); 2798c2ecf20Sopenharmony_ci snd_pcm_chip(pcm) = dev; 2808c2ecf20Sopenharmony_ci pcm->info_flags = 0; 2818c2ecf20Sopenharmony_ci strscpy(pcm->name, "tw686x PCM", sizeof(pcm->name)); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci for (i = 0, ss = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; 2848c2ecf20Sopenharmony_ci ss; ss = ss->next, i++) 2858c2ecf20Sopenharmony_ci snprintf(ss->name, sizeof(ss->name), "vch%u audio", i); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, 2888c2ecf20Sopenharmony_ci SNDRV_DMA_TYPE_DEV, 2898c2ecf20Sopenharmony_ci &dev->pci_dev->dev, 2908c2ecf20Sopenharmony_ci TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX, 2918c2ecf20Sopenharmony_ci TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX); 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic void tw686x_audio_dma_free(struct tw686x_dev *dev, 2968c2ecf20Sopenharmony_ci struct tw686x_audio_channel *ac) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci int pb; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci for (pb = 0; pb < 2; pb++) { 3018c2ecf20Sopenharmony_ci if (!ac->dma_descs[pb].virt) 3028c2ecf20Sopenharmony_ci continue; 3038c2ecf20Sopenharmony_ci pci_free_consistent(dev->pci_dev, ac->dma_descs[pb].size, 3048c2ecf20Sopenharmony_ci ac->dma_descs[pb].virt, 3058c2ecf20Sopenharmony_ci ac->dma_descs[pb].phys); 3068c2ecf20Sopenharmony_ci ac->dma_descs[pb].virt = NULL; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic int tw686x_audio_dma_alloc(struct tw686x_dev *dev, 3118c2ecf20Sopenharmony_ci struct tw686x_audio_channel *ac) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci int pb; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* 3168c2ecf20Sopenharmony_ci * In the memcpy DMA mode we allocate a consistent buffer 3178c2ecf20Sopenharmony_ci * and use it for the DMA capture. Otherwise, DMA 3188c2ecf20Sopenharmony_ci * acts on the ALSA buffers as received in pcm_prepare. 3198c2ecf20Sopenharmony_ci */ 3208c2ecf20Sopenharmony_ci if (dev->dma_mode != TW686X_DMA_MODE_MEMCPY) 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci for (pb = 0; pb < 2; pb++) { 3248c2ecf20Sopenharmony_ci u32 reg = pb ? ADMA_B_ADDR[ac->ch] : ADMA_P_ADDR[ac->ch]; 3258c2ecf20Sopenharmony_ci void *virt; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci virt = pci_alloc_consistent(dev->pci_dev, AUDIO_DMA_SIZE_MAX, 3288c2ecf20Sopenharmony_ci &ac->dma_descs[pb].phys); 3298c2ecf20Sopenharmony_ci if (!virt) { 3308c2ecf20Sopenharmony_ci dev_err(&dev->pci_dev->dev, 3318c2ecf20Sopenharmony_ci "dma%d: unable to allocate audio DMA %s-buffer\n", 3328c2ecf20Sopenharmony_ci ac->ch, pb ? "B" : "P"); 3338c2ecf20Sopenharmony_ci return -ENOMEM; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci ac->dma_descs[pb].virt = virt; 3368c2ecf20Sopenharmony_ci ac->dma_descs[pb].size = AUDIO_DMA_SIZE_MAX; 3378c2ecf20Sopenharmony_ci reg_write(dev, reg, ac->dma_descs[pb].phys); 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_civoid tw686x_audio_free(struct tw686x_dev *dev) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci unsigned long flags; 3458c2ecf20Sopenharmony_ci u32 dma_ch_mask; 3468c2ecf20Sopenharmony_ci u32 dma_cmd; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 3498c2ecf20Sopenharmony_ci dma_cmd = reg_read(dev, DMA_CMD); 3508c2ecf20Sopenharmony_ci dma_ch_mask = reg_read(dev, DMA_CHANNEL_ENABLE); 3518c2ecf20Sopenharmony_ci reg_write(dev, DMA_CMD, dma_cmd & ~0xff00); 3528c2ecf20Sopenharmony_ci reg_write(dev, DMA_CHANNEL_ENABLE, dma_ch_mask & ~0xff00); 3538c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (!dev->snd_card) 3568c2ecf20Sopenharmony_ci return; 3578c2ecf20Sopenharmony_ci snd_card_free(dev->snd_card); 3588c2ecf20Sopenharmony_ci dev->snd_card = NULL; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ciint tw686x_audio_init(struct tw686x_dev *dev) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = dev->pci_dev; 3648c2ecf20Sopenharmony_ci struct snd_card *card; 3658c2ecf20Sopenharmony_ci int err, ch; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* Enable external audio */ 3688c2ecf20Sopenharmony_ci reg_write(dev, AUDIO_CONTROL1, BIT(0)); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci err = snd_card_new(&pci_dev->dev, SNDRV_DEFAULT_IDX1, 3718c2ecf20Sopenharmony_ci SNDRV_DEFAULT_STR1, 3728c2ecf20Sopenharmony_ci THIS_MODULE, 0, &card); 3738c2ecf20Sopenharmony_ci if (err < 0) 3748c2ecf20Sopenharmony_ci return err; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci dev->snd_card = card; 3778c2ecf20Sopenharmony_ci strscpy(card->driver, "tw686x", sizeof(card->driver)); 3788c2ecf20Sopenharmony_ci strscpy(card->shortname, "tw686x", sizeof(card->shortname)); 3798c2ecf20Sopenharmony_ci strscpy(card->longname, pci_name(pci_dev), sizeof(card->longname)); 3808c2ecf20Sopenharmony_ci snd_card_set_dev(card, &pci_dev->dev); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci for (ch = 0; ch < max_channels(dev); ch++) { 3838c2ecf20Sopenharmony_ci struct tw686x_audio_channel *ac; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci ac = &dev->audio_channels[ch]; 3868c2ecf20Sopenharmony_ci spin_lock_init(&ac->lock); 3878c2ecf20Sopenharmony_ci ac->dev = dev; 3888c2ecf20Sopenharmony_ci ac->ch = ch; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci err = tw686x_audio_dma_alloc(dev, ac); 3918c2ecf20Sopenharmony_ci if (err < 0) 3928c2ecf20Sopenharmony_ci goto err_cleanup; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci err = tw686x_snd_pcm_init(dev); 3968c2ecf20Sopenharmony_ci if (err < 0) 3978c2ecf20Sopenharmony_ci goto err_cleanup; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci err = snd_card_register(card); 4008c2ecf20Sopenharmony_ci if (!err) 4018c2ecf20Sopenharmony_ci return 0; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cierr_cleanup: 4048c2ecf20Sopenharmony_ci for (ch = 0; ch < max_channels(dev); ch++) { 4058c2ecf20Sopenharmony_ci if (!dev->audio_channels[ch].dev) 4068c2ecf20Sopenharmony_ci continue; 4078c2ecf20Sopenharmony_ci tw686x_audio_dma_free(dev, &dev->audio_channels[ch]); 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci snd_card_free(card); 4108c2ecf20Sopenharmony_ci dev->snd_card = NULL; 4118c2ecf20Sopenharmony_ci return err; 4128c2ecf20Sopenharmony_ci} 413