162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2015 VanguardiaSur - www.vanguardiasur.com.ar 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Based on the audio support from the tw6869 driver: 662306a36Sopenharmony_ci * Copyright 2015 www.starterkit.ru <info@starterkit.ru> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Based on: 962306a36Sopenharmony_ci * Driver for Intersil|Techwell TW6869 based DVR cards 1062306a36Sopenharmony_ci * (c) 2011-12 liran <jli11@intersil.com> [Intersil|Techwell China] 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/types.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/kmod.h> 1862306a36Sopenharmony_ci#include <linux/mutex.h> 1962306a36Sopenharmony_ci#include <linux/pci.h> 2062306a36Sopenharmony_ci#include <linux/delay.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <sound/core.h> 2362306a36Sopenharmony_ci#include <sound/initval.h> 2462306a36Sopenharmony_ci#include <sound/pcm.h> 2562306a36Sopenharmony_ci#include <sound/control.h> 2662306a36Sopenharmony_ci#include "tw686x.h" 2762306a36Sopenharmony_ci#include "tw686x-regs.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define AUDIO_CHANNEL_OFFSET 8 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_civoid tw686x_audio_irq(struct tw686x_dev *dev, unsigned long requests, 3262306a36Sopenharmony_ci unsigned int pb_status) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci unsigned long flags; 3562306a36Sopenharmony_ci unsigned int ch, pb; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci for_each_set_bit(ch, &requests, max_channels(dev)) { 3862306a36Sopenharmony_ci struct tw686x_audio_channel *ac = &dev->audio_channels[ch]; 3962306a36Sopenharmony_ci struct tw686x_audio_buf *done = NULL; 4062306a36Sopenharmony_ci struct tw686x_audio_buf *next = NULL; 4162306a36Sopenharmony_ci struct tw686x_dma_desc *desc; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci pb = !!(pb_status & BIT(AUDIO_CHANNEL_OFFSET + ch)); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci spin_lock_irqsave(&ac->lock, flags); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* Sanity check */ 4862306a36Sopenharmony_ci if (!ac->ss || !ac->curr_bufs[0] || !ac->curr_bufs[1]) { 4962306a36Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 5062306a36Sopenharmony_ci continue; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (!list_empty(&ac->buf_list)) { 5462306a36Sopenharmony_ci next = list_first_entry(&ac->buf_list, 5562306a36Sopenharmony_ci struct tw686x_audio_buf, list); 5662306a36Sopenharmony_ci list_move_tail(&next->list, &ac->buf_list); 5762306a36Sopenharmony_ci done = ac->curr_bufs[!pb]; 5862306a36Sopenharmony_ci ac->curr_bufs[pb] = next; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (!done) 6362306a36Sopenharmony_ci continue; 6462306a36Sopenharmony_ci /* 6562306a36Sopenharmony_ci * Checking for a non-nil dma_desc[pb]->virt buffer is 6662306a36Sopenharmony_ci * the same as checking for memcpy DMA mode. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci desc = &ac->dma_descs[pb]; 6962306a36Sopenharmony_ci if (desc->virt) { 7062306a36Sopenharmony_ci memcpy(done->virt, desc->virt, 7162306a36Sopenharmony_ci dev->period_size); 7262306a36Sopenharmony_ci } else { 7362306a36Sopenharmony_ci u32 reg = pb ? ADMA_B_ADDR[ch] : ADMA_P_ADDR[ch]; 7462306a36Sopenharmony_ci reg_write(dev, reg, next->dma); 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci ac->ptr = done->dma - ac->buf[0].dma; 7762306a36Sopenharmony_ci snd_pcm_period_elapsed(ac->ss); 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* 8262306a36Sopenharmony_ci * Audio parameters are global and shared among all 8362306a36Sopenharmony_ci * capture channels. The driver prevents changes to 8462306a36Sopenharmony_ci * the parameters if any audio channel is capturing. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_cistatic const struct snd_pcm_hardware tw686x_capture_hw = { 8762306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | 8862306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 8962306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 9062306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 9162306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 9262306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 9362306a36Sopenharmony_ci .rate_min = 8000, 9462306a36Sopenharmony_ci .rate_max = 48000, 9562306a36Sopenharmony_ci .channels_min = 1, 9662306a36Sopenharmony_ci .channels_max = 1, 9762306a36Sopenharmony_ci .buffer_bytes_max = TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX, 9862306a36Sopenharmony_ci .period_bytes_min = AUDIO_DMA_SIZE_MIN, 9962306a36Sopenharmony_ci .period_bytes_max = AUDIO_DMA_SIZE_MAX, 10062306a36Sopenharmony_ci .periods_min = TW686X_AUDIO_PERIODS_MIN, 10162306a36Sopenharmony_ci .periods_max = TW686X_AUDIO_PERIODS_MAX, 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int tw686x_pcm_open(struct snd_pcm_substream *ss) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct tw686x_dev *dev = snd_pcm_substream_chip(ss); 10762306a36Sopenharmony_ci struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; 10862306a36Sopenharmony_ci struct snd_pcm_runtime *rt = ss->runtime; 10962306a36Sopenharmony_ci int err; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci ac->ss = ss; 11262306a36Sopenharmony_ci rt->hw = tw686x_capture_hw; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); 11562306a36Sopenharmony_ci if (err < 0) 11662306a36Sopenharmony_ci return err; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int tw686x_pcm_close(struct snd_pcm_substream *ss) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct tw686x_dev *dev = snd_pcm_substream_chip(ss); 12462306a36Sopenharmony_ci struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci ac->ss = NULL; 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int tw686x_pcm_prepare(struct snd_pcm_substream *ss) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct tw686x_dev *dev = snd_pcm_substream_chip(ss); 13362306a36Sopenharmony_ci struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; 13462306a36Sopenharmony_ci struct snd_pcm_runtime *rt = ss->runtime; 13562306a36Sopenharmony_ci unsigned int period_size = snd_pcm_lib_period_bytes(ss); 13662306a36Sopenharmony_ci struct tw686x_audio_buf *p_buf, *b_buf; 13762306a36Sopenharmony_ci unsigned long flags; 13862306a36Sopenharmony_ci int i; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 14162306a36Sopenharmony_ci /* 14262306a36Sopenharmony_ci * Given the audio parameters are global (i.e. shared across 14362306a36Sopenharmony_ci * DMA channels), we need to check new params are allowed. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci if (((dev->audio_rate != rt->rate) || 14662306a36Sopenharmony_ci (dev->period_size != period_size)) && dev->audio_enabled) 14762306a36Sopenharmony_ci goto err_audio_busy; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch); 15062306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (dev->audio_rate != rt->rate) { 15362306a36Sopenharmony_ci u32 reg; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci dev->audio_rate = rt->rate; 15662306a36Sopenharmony_ci reg = ((125000000 / rt->rate) << 16) + 15762306a36Sopenharmony_ci ((125000000 % rt->rate) << 16) / rt->rate; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci reg_write(dev, AUDIO_CONTROL2, reg); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (dev->period_size != period_size) { 16362306a36Sopenharmony_ci u32 reg; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci dev->period_size = period_size; 16662306a36Sopenharmony_ci reg = reg_read(dev, AUDIO_CONTROL1); 16762306a36Sopenharmony_ci reg &= ~(AUDIO_DMA_SIZE_MASK << AUDIO_DMA_SIZE_SHIFT); 16862306a36Sopenharmony_ci reg |= period_size << AUDIO_DMA_SIZE_SHIFT; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci reg_write(dev, AUDIO_CONTROL1, reg); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (rt->periods < TW686X_AUDIO_PERIODS_MIN || 17462306a36Sopenharmony_ci rt->periods > TW686X_AUDIO_PERIODS_MAX) 17562306a36Sopenharmony_ci return -EINVAL; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci spin_lock_irqsave(&ac->lock, flags); 17862306a36Sopenharmony_ci INIT_LIST_HEAD(&ac->buf_list); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci for (i = 0; i < rt->periods; i++) { 18162306a36Sopenharmony_ci ac->buf[i].dma = rt->dma_addr + period_size * i; 18262306a36Sopenharmony_ci ac->buf[i].virt = rt->dma_area + period_size * i; 18362306a36Sopenharmony_ci INIT_LIST_HEAD(&ac->buf[i].list); 18462306a36Sopenharmony_ci list_add_tail(&ac->buf[i].list, &ac->buf_list); 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci p_buf = list_first_entry(&ac->buf_list, struct tw686x_audio_buf, list); 18862306a36Sopenharmony_ci list_move_tail(&p_buf->list, &ac->buf_list); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci b_buf = list_first_entry(&ac->buf_list, struct tw686x_audio_buf, list); 19162306a36Sopenharmony_ci list_move_tail(&b_buf->list, &ac->buf_list); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci ac->curr_bufs[0] = p_buf; 19462306a36Sopenharmony_ci ac->curr_bufs[1] = b_buf; 19562306a36Sopenharmony_ci ac->ptr = 0; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (dev->dma_mode != TW686X_DMA_MODE_MEMCPY) { 19862306a36Sopenharmony_ci reg_write(dev, ADMA_P_ADDR[ac->ch], p_buf->dma); 19962306a36Sopenharmony_ci reg_write(dev, ADMA_B_ADDR[ac->ch], b_buf->dma); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cierr_audio_busy: 20762306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 20862306a36Sopenharmony_ci return -EBUSY; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int tw686x_pcm_trigger(struct snd_pcm_substream *ss, int cmd) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct tw686x_dev *dev = snd_pcm_substream_chip(ss); 21462306a36Sopenharmony_ci struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; 21562306a36Sopenharmony_ci unsigned long flags; 21662306a36Sopenharmony_ci int err = 0; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci switch (cmd) { 21962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 22062306a36Sopenharmony_ci if (ac->curr_bufs[0] && ac->curr_bufs[1]) { 22162306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 22262306a36Sopenharmony_ci dev->audio_enabled = 1; 22362306a36Sopenharmony_ci tw686x_enable_channel(dev, 22462306a36Sopenharmony_ci AUDIO_CHANNEL_OFFSET + ac->ch); 22562306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci mod_timer(&dev->dma_delay_timer, 22862306a36Sopenharmony_ci jiffies + msecs_to_jiffies(100)); 22962306a36Sopenharmony_ci } else { 23062306a36Sopenharmony_ci err = -EIO; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 23462306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 23562306a36Sopenharmony_ci dev->audio_enabled = 0; 23662306a36Sopenharmony_ci tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch); 23762306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci spin_lock_irqsave(&ac->lock, flags); 24062306a36Sopenharmony_ci ac->curr_bufs[0] = NULL; 24162306a36Sopenharmony_ci ac->curr_bufs[1] = NULL; 24262306a36Sopenharmony_ci spin_unlock_irqrestore(&ac->lock, flags); 24362306a36Sopenharmony_ci break; 24462306a36Sopenharmony_ci default: 24562306a36Sopenharmony_ci err = -EINVAL; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci return err; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic snd_pcm_uframes_t tw686x_pcm_pointer(struct snd_pcm_substream *ss) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct tw686x_dev *dev = snd_pcm_substream_chip(ss); 25362306a36Sopenharmony_ci struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number]; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci return bytes_to_frames(ss->runtime, ac->ptr); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic const struct snd_pcm_ops tw686x_pcm_ops = { 25962306a36Sopenharmony_ci .open = tw686x_pcm_open, 26062306a36Sopenharmony_ci .close = tw686x_pcm_close, 26162306a36Sopenharmony_ci .prepare = tw686x_pcm_prepare, 26262306a36Sopenharmony_ci .trigger = tw686x_pcm_trigger, 26362306a36Sopenharmony_ci .pointer = tw686x_pcm_pointer, 26462306a36Sopenharmony_ci}; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic int tw686x_snd_pcm_init(struct tw686x_dev *dev) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct snd_card *card = dev->snd_card; 26962306a36Sopenharmony_ci struct snd_pcm *pcm; 27062306a36Sopenharmony_ci struct snd_pcm_substream *ss; 27162306a36Sopenharmony_ci unsigned int i; 27262306a36Sopenharmony_ci int err; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci err = snd_pcm_new(card, card->driver, 0, 0, max_channels(dev), &pcm); 27562306a36Sopenharmony_ci if (err < 0) 27662306a36Sopenharmony_ci return err; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &tw686x_pcm_ops); 27962306a36Sopenharmony_ci snd_pcm_chip(pcm) = dev; 28062306a36Sopenharmony_ci pcm->info_flags = 0; 28162306a36Sopenharmony_ci strscpy(pcm->name, "tw686x PCM", sizeof(pcm->name)); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci for (i = 0, ss = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; 28462306a36Sopenharmony_ci ss; ss = ss->next, i++) 28562306a36Sopenharmony_ci snprintf(ss->name, sizeof(ss->name), "vch%u audio", i); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, 28862306a36Sopenharmony_ci SNDRV_DMA_TYPE_DEV, 28962306a36Sopenharmony_ci &dev->pci_dev->dev, 29062306a36Sopenharmony_ci TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX, 29162306a36Sopenharmony_ci TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX); 29262306a36Sopenharmony_ci return 0; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic void tw686x_audio_dma_free(struct tw686x_dev *dev, 29662306a36Sopenharmony_ci struct tw686x_audio_channel *ac) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci int pb; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci for (pb = 0; pb < 2; pb++) { 30162306a36Sopenharmony_ci if (!ac->dma_descs[pb].virt) 30262306a36Sopenharmony_ci continue; 30362306a36Sopenharmony_ci dma_free_coherent(&dev->pci_dev->dev, ac->dma_descs[pb].size, 30462306a36Sopenharmony_ci ac->dma_descs[pb].virt, 30562306a36Sopenharmony_ci ac->dma_descs[pb].phys); 30662306a36Sopenharmony_ci ac->dma_descs[pb].virt = NULL; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic int tw686x_audio_dma_alloc(struct tw686x_dev *dev, 31162306a36Sopenharmony_ci struct tw686x_audio_channel *ac) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci int pb; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* 31662306a36Sopenharmony_ci * In the memcpy DMA mode we allocate a coherent buffer 31762306a36Sopenharmony_ci * and use it for the DMA capture. Otherwise, DMA 31862306a36Sopenharmony_ci * acts on the ALSA buffers as received in pcm_prepare. 31962306a36Sopenharmony_ci */ 32062306a36Sopenharmony_ci if (dev->dma_mode != TW686X_DMA_MODE_MEMCPY) 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci for (pb = 0; pb < 2; pb++) { 32462306a36Sopenharmony_ci u32 reg = pb ? ADMA_B_ADDR[ac->ch] : ADMA_P_ADDR[ac->ch]; 32562306a36Sopenharmony_ci void *virt; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci virt = dma_alloc_coherent(&dev->pci_dev->dev, 32862306a36Sopenharmony_ci AUDIO_DMA_SIZE_MAX, 32962306a36Sopenharmony_ci &ac->dma_descs[pb].phys, GFP_KERNEL); 33062306a36Sopenharmony_ci if (!virt) { 33162306a36Sopenharmony_ci dev_err(&dev->pci_dev->dev, 33262306a36Sopenharmony_ci "dma%d: unable to allocate audio DMA %s-buffer\n", 33362306a36Sopenharmony_ci ac->ch, pb ? "B" : "P"); 33462306a36Sopenharmony_ci return -ENOMEM; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci ac->dma_descs[pb].virt = virt; 33762306a36Sopenharmony_ci ac->dma_descs[pb].size = AUDIO_DMA_SIZE_MAX; 33862306a36Sopenharmony_ci reg_write(dev, reg, ac->dma_descs[pb].phys); 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci return 0; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_civoid tw686x_audio_free(struct tw686x_dev *dev) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci unsigned long flags; 34662306a36Sopenharmony_ci u32 dma_ch_mask; 34762306a36Sopenharmony_ci u32 dma_cmd; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 35062306a36Sopenharmony_ci dma_cmd = reg_read(dev, DMA_CMD); 35162306a36Sopenharmony_ci dma_ch_mask = reg_read(dev, DMA_CHANNEL_ENABLE); 35262306a36Sopenharmony_ci reg_write(dev, DMA_CMD, dma_cmd & ~0xff00); 35362306a36Sopenharmony_ci reg_write(dev, DMA_CHANNEL_ENABLE, dma_ch_mask & ~0xff00); 35462306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (!dev->snd_card) 35762306a36Sopenharmony_ci return; 35862306a36Sopenharmony_ci snd_card_free(dev->snd_card); 35962306a36Sopenharmony_ci dev->snd_card = NULL; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ciint tw686x_audio_init(struct tw686x_dev *dev) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci struct pci_dev *pci_dev = dev->pci_dev; 36562306a36Sopenharmony_ci struct snd_card *card; 36662306a36Sopenharmony_ci int err, ch; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* Enable external audio */ 36962306a36Sopenharmony_ci reg_write(dev, AUDIO_CONTROL1, BIT(0)); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci err = snd_card_new(&pci_dev->dev, SNDRV_DEFAULT_IDX1, 37262306a36Sopenharmony_ci SNDRV_DEFAULT_STR1, 37362306a36Sopenharmony_ci THIS_MODULE, 0, &card); 37462306a36Sopenharmony_ci if (err < 0) 37562306a36Sopenharmony_ci return err; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci dev->snd_card = card; 37862306a36Sopenharmony_ci strscpy(card->driver, "tw686x", sizeof(card->driver)); 37962306a36Sopenharmony_ci strscpy(card->shortname, "tw686x", sizeof(card->shortname)); 38062306a36Sopenharmony_ci strscpy(card->longname, pci_name(pci_dev), sizeof(card->longname)); 38162306a36Sopenharmony_ci snd_card_set_dev(card, &pci_dev->dev); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci for (ch = 0; ch < max_channels(dev); ch++) { 38462306a36Sopenharmony_ci struct tw686x_audio_channel *ac; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci ac = &dev->audio_channels[ch]; 38762306a36Sopenharmony_ci spin_lock_init(&ac->lock); 38862306a36Sopenharmony_ci ac->dev = dev; 38962306a36Sopenharmony_ci ac->ch = ch; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci err = tw686x_audio_dma_alloc(dev, ac); 39262306a36Sopenharmony_ci if (err < 0) 39362306a36Sopenharmony_ci goto err_cleanup; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci err = tw686x_snd_pcm_init(dev); 39762306a36Sopenharmony_ci if (err < 0) 39862306a36Sopenharmony_ci goto err_cleanup; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci err = snd_card_register(card); 40162306a36Sopenharmony_ci if (!err) 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cierr_cleanup: 40562306a36Sopenharmony_ci for (ch = 0; ch < max_channels(dev); ch++) { 40662306a36Sopenharmony_ci if (!dev->audio_channels[ch].dev) 40762306a36Sopenharmony_ci continue; 40862306a36Sopenharmony_ci tw686x_audio_dma_free(dev, &dev->audio_channels[ch]); 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci snd_card_free(card); 41162306a36Sopenharmony_ci dev->snd_card = NULL; 41262306a36Sopenharmony_ci return err; 41362306a36Sopenharmony_ci} 414