brintos

brintos / linux-shallow public Read only

0
0
Text · 10.6 KiB · 1ae3845 Raw
414 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2015 VanguardiaSur - www.vanguardiasur.com.ar4 *5 * Based on the audio support from the tw6869 driver:6 * Copyright 2015 www.starterkit.ru <info@starterkit.ru>7 *8 * Based on:9 * Driver for Intersil|Techwell TW6869 based DVR cards10 * (c) 2011-12 liran <jli11@intersil.com> [Intersil|Techwell China]11 */12 13#include <linux/types.h>14#include <linux/kernel.h>15#include <linux/module.h>16#include <linux/init.h>17#include <linux/kmod.h>18#include <linux/mutex.h>19#include <linux/pci.h>20#include <linux/delay.h>21 22#include <sound/core.h>23#include <sound/initval.h>24#include <sound/pcm.h>25#include <sound/control.h>26#include "tw686x.h"27#include "tw686x-regs.h"28 29#define AUDIO_CHANNEL_OFFSET 830 31void tw686x_audio_irq(struct tw686x_dev *dev, unsigned long requests,32		      unsigned int pb_status)33{34	unsigned long flags;35	unsigned int ch, pb;36 37	for_each_set_bit(ch, &requests, max_channels(dev)) {38		struct tw686x_audio_channel *ac = &dev->audio_channels[ch];39		struct tw686x_audio_buf *done = NULL;40		struct tw686x_audio_buf *next = NULL;41		struct tw686x_dma_desc *desc;42 43		pb = !!(pb_status & BIT(AUDIO_CHANNEL_OFFSET + ch));44 45		spin_lock_irqsave(&ac->lock, flags);46 47		/* Sanity check */48		if (!ac->ss || !ac->curr_bufs[0] || !ac->curr_bufs[1]) {49			spin_unlock_irqrestore(&ac->lock, flags);50			continue;51		}52 53		if (!list_empty(&ac->buf_list)) {54			next = list_first_entry(&ac->buf_list,55					struct tw686x_audio_buf, list);56			list_move_tail(&next->list, &ac->buf_list);57			done = ac->curr_bufs[!pb];58			ac->curr_bufs[pb] = next;59		}60		spin_unlock_irqrestore(&ac->lock, flags);61 62		if (!done)63			continue;64		/*65		 * Checking for a non-nil dma_desc[pb]->virt buffer is66		 * the same as checking for memcpy DMA mode.67		 */68		desc = &ac->dma_descs[pb];69		if (desc->virt) {70			memcpy(done->virt, desc->virt,71			       dev->period_size);72		} else {73			u32 reg = pb ? ADMA_B_ADDR[ch] : ADMA_P_ADDR[ch];74			reg_write(dev, reg, next->dma);75		}76		ac->ptr = done->dma - ac->buf[0].dma;77		snd_pcm_period_elapsed(ac->ss);78	}79}80 81/*82 * Audio parameters are global and shared among all83 * capture channels. The driver prevents changes to84 * the parameters if any audio channel is capturing.85 */86static const struct snd_pcm_hardware tw686x_capture_hw = {87	.info			= (SNDRV_PCM_INFO_MMAP |88				   SNDRV_PCM_INFO_INTERLEAVED |89				   SNDRV_PCM_INFO_BLOCK_TRANSFER |90				   SNDRV_PCM_INFO_MMAP_VALID),91	.formats		= SNDRV_PCM_FMTBIT_S16_LE,92	.rates			= SNDRV_PCM_RATE_8000_48000,93	.rate_min		= 8000,94	.rate_max		= 48000,95	.channels_min		= 1,96	.channels_max		= 1,97	.buffer_bytes_max	= TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX,98	.period_bytes_min	= AUDIO_DMA_SIZE_MIN,99	.period_bytes_max	= AUDIO_DMA_SIZE_MAX,100	.periods_min		= TW686X_AUDIO_PERIODS_MIN,101	.periods_max		= TW686X_AUDIO_PERIODS_MAX,102};103 104static int tw686x_pcm_open(struct snd_pcm_substream *ss)105{106	struct tw686x_dev *dev = snd_pcm_substream_chip(ss);107	struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number];108	struct snd_pcm_runtime *rt = ss->runtime;109	int err;110 111	ac->ss = ss;112	rt->hw = tw686x_capture_hw;113 114	err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS);115	if (err < 0)116		return err;117 118	return 0;119}120 121static int tw686x_pcm_close(struct snd_pcm_substream *ss)122{123	struct tw686x_dev *dev = snd_pcm_substream_chip(ss);124	struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number];125 126	ac->ss = NULL;127	return 0;128}129 130static int tw686x_pcm_prepare(struct snd_pcm_substream *ss)131{132	struct tw686x_dev *dev = snd_pcm_substream_chip(ss);133	struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number];134	struct snd_pcm_runtime *rt = ss->runtime;135	unsigned int period_size = snd_pcm_lib_period_bytes(ss);136	struct tw686x_audio_buf *p_buf, *b_buf;137	unsigned long flags;138	int i;139 140	spin_lock_irqsave(&dev->lock, flags);141	/*142	 * Given the audio parameters are global (i.e. shared across143	 * DMA channels), we need to check new params are allowed.144	 */145	if (((dev->audio_rate != rt->rate) ||146	     (dev->period_size != period_size)) && dev->audio_enabled)147		goto err_audio_busy;148 149	tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch);150	spin_unlock_irqrestore(&dev->lock, flags);151 152	if (dev->audio_rate != rt->rate) {153		u32 reg;154 155		dev->audio_rate = rt->rate;156		reg = ((125000000 / rt->rate) << 16) +157		       ((125000000 % rt->rate) << 16) / rt->rate;158 159		reg_write(dev, AUDIO_CONTROL2, reg);160	}161 162	if (dev->period_size != period_size) {163		u32 reg;164 165		dev->period_size = period_size;166		reg = reg_read(dev, AUDIO_CONTROL1);167		reg &= ~(AUDIO_DMA_SIZE_MASK << AUDIO_DMA_SIZE_SHIFT);168		reg |= period_size << AUDIO_DMA_SIZE_SHIFT;169 170		reg_write(dev, AUDIO_CONTROL1, reg);171	}172 173	if (rt->periods < TW686X_AUDIO_PERIODS_MIN ||174	    rt->periods > TW686X_AUDIO_PERIODS_MAX)175		return -EINVAL;176 177	spin_lock_irqsave(&ac->lock, flags);178	INIT_LIST_HEAD(&ac->buf_list);179 180	for (i = 0; i < rt->periods; i++) {181		ac->buf[i].dma = rt->dma_addr + period_size * i;182		ac->buf[i].virt = rt->dma_area + period_size * i;183		INIT_LIST_HEAD(&ac->buf[i].list);184		list_add_tail(&ac->buf[i].list, &ac->buf_list);185	}186 187	p_buf =	list_first_entry(&ac->buf_list, struct tw686x_audio_buf, list);188	list_move_tail(&p_buf->list, &ac->buf_list);189 190	b_buf =	list_first_entry(&ac->buf_list, struct tw686x_audio_buf, list);191	list_move_tail(&b_buf->list, &ac->buf_list);192 193	ac->curr_bufs[0] = p_buf;194	ac->curr_bufs[1] = b_buf;195	ac->ptr = 0;196 197	if (dev->dma_mode != TW686X_DMA_MODE_MEMCPY) {198		reg_write(dev, ADMA_P_ADDR[ac->ch], p_buf->dma);199		reg_write(dev, ADMA_B_ADDR[ac->ch], b_buf->dma);200	}201 202	spin_unlock_irqrestore(&ac->lock, flags);203 204	return 0;205 206err_audio_busy:207	spin_unlock_irqrestore(&dev->lock, flags);208	return -EBUSY;209}210 211static int tw686x_pcm_trigger(struct snd_pcm_substream *ss, int cmd)212{213	struct tw686x_dev *dev = snd_pcm_substream_chip(ss);214	struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number];215	unsigned long flags;216	int err = 0;217 218	switch (cmd) {219	case SNDRV_PCM_TRIGGER_START:220		if (ac->curr_bufs[0] && ac->curr_bufs[1]) {221			spin_lock_irqsave(&dev->lock, flags);222			dev->audio_enabled = 1;223			tw686x_enable_channel(dev,224				AUDIO_CHANNEL_OFFSET + ac->ch);225			spin_unlock_irqrestore(&dev->lock, flags);226 227			mod_timer(&dev->dma_delay_timer,228				  jiffies + msecs_to_jiffies(100));229		} else {230			err = -EIO;231		}232		break;233	case SNDRV_PCM_TRIGGER_STOP:234		spin_lock_irqsave(&dev->lock, flags);235		dev->audio_enabled = 0;236		tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch);237		spin_unlock_irqrestore(&dev->lock, flags);238 239		spin_lock_irqsave(&ac->lock, flags);240		ac->curr_bufs[0] = NULL;241		ac->curr_bufs[1] = NULL;242		spin_unlock_irqrestore(&ac->lock, flags);243		break;244	default:245		err = -EINVAL;246	}247	return err;248}249 250static snd_pcm_uframes_t tw686x_pcm_pointer(struct snd_pcm_substream *ss)251{252	struct tw686x_dev *dev = snd_pcm_substream_chip(ss);253	struct tw686x_audio_channel *ac = &dev->audio_channels[ss->number];254 255	return bytes_to_frames(ss->runtime, ac->ptr);256}257 258static const struct snd_pcm_ops tw686x_pcm_ops = {259	.open = tw686x_pcm_open,260	.close = tw686x_pcm_close,261	.prepare = tw686x_pcm_prepare,262	.trigger = tw686x_pcm_trigger,263	.pointer = tw686x_pcm_pointer,264};265 266static int tw686x_snd_pcm_init(struct tw686x_dev *dev)267{268	struct snd_card *card = dev->snd_card;269	struct snd_pcm *pcm;270	struct snd_pcm_substream *ss;271	unsigned int i;272	int err;273 274	err = snd_pcm_new(card, card->driver, 0, 0, max_channels(dev), &pcm);275	if (err < 0)276		return err;277 278	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &tw686x_pcm_ops);279	snd_pcm_chip(pcm) = dev;280	pcm->info_flags = 0;281	strscpy(pcm->name, "tw686x PCM", sizeof(pcm->name));282 283	for (i = 0, ss = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;284	     ss; ss = ss->next, i++)285		snprintf(ss->name, sizeof(ss->name), "vch%u audio", i);286 287	snd_pcm_set_managed_buffer_all(pcm,288				SNDRV_DMA_TYPE_DEV,289				&dev->pci_dev->dev,290				TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX,291				TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX);292	return 0;293}294 295static void tw686x_audio_dma_free(struct tw686x_dev *dev,296				  struct tw686x_audio_channel *ac)297{298	int pb;299 300	for (pb = 0; pb < 2; pb++) {301		if (!ac->dma_descs[pb].virt)302			continue;303		dma_free_coherent(&dev->pci_dev->dev, ac->dma_descs[pb].size,304				  ac->dma_descs[pb].virt,305				  ac->dma_descs[pb].phys);306		ac->dma_descs[pb].virt = NULL;307	}308}309 310static int tw686x_audio_dma_alloc(struct tw686x_dev *dev,311				  struct tw686x_audio_channel *ac)312{313	int pb;314 315	/*316	 * In the memcpy DMA mode we allocate a coherent buffer317	 * and use it for the DMA capture. Otherwise, DMA318	 * acts on the ALSA buffers as received in pcm_prepare.319	 */320	if (dev->dma_mode != TW686X_DMA_MODE_MEMCPY)321		return 0;322 323	for (pb = 0; pb < 2; pb++) {324		u32 reg = pb ? ADMA_B_ADDR[ac->ch] : ADMA_P_ADDR[ac->ch];325		void *virt;326 327		virt = dma_alloc_coherent(&dev->pci_dev->dev,328					  AUDIO_DMA_SIZE_MAX,329					  &ac->dma_descs[pb].phys, GFP_KERNEL);330		if (!virt) {331			dev_err(&dev->pci_dev->dev,332				"dma%d: unable to allocate audio DMA %s-buffer\n",333				ac->ch, pb ? "B" : "P");334			return -ENOMEM;335		}336		ac->dma_descs[pb].virt = virt;337		ac->dma_descs[pb].size = AUDIO_DMA_SIZE_MAX;338		reg_write(dev, reg, ac->dma_descs[pb].phys);339	}340	return 0;341}342 343void tw686x_audio_free(struct tw686x_dev *dev)344{345	unsigned long flags;346	u32 dma_ch_mask;347	u32 dma_cmd;348 349	spin_lock_irqsave(&dev->lock, flags);350	dma_cmd = reg_read(dev, DMA_CMD);351	dma_ch_mask = reg_read(dev, DMA_CHANNEL_ENABLE);352	reg_write(dev, DMA_CMD, dma_cmd & ~0xff00);353	reg_write(dev, DMA_CHANNEL_ENABLE, dma_ch_mask & ~0xff00);354	spin_unlock_irqrestore(&dev->lock, flags);355 356	if (!dev->snd_card)357		return;358	snd_card_free(dev->snd_card);359	dev->snd_card = NULL;360}361 362int tw686x_audio_init(struct tw686x_dev *dev)363{364	struct pci_dev *pci_dev = dev->pci_dev;365	struct snd_card *card;366	int err, ch;367 368	/* Enable external audio */369	reg_write(dev, AUDIO_CONTROL1, BIT(0));370 371	err = snd_card_new(&pci_dev->dev, SNDRV_DEFAULT_IDX1,372			   SNDRV_DEFAULT_STR1,373			   THIS_MODULE, 0, &card);374	if (err < 0)375		return err;376 377	dev->snd_card = card;378	strscpy(card->driver, "tw686x", sizeof(card->driver));379	strscpy(card->shortname, "tw686x", sizeof(card->shortname));380	strscpy(card->longname, pci_name(pci_dev), sizeof(card->longname));381	snd_card_set_dev(card, &pci_dev->dev);382 383	for (ch = 0; ch < max_channels(dev); ch++) {384		struct tw686x_audio_channel *ac;385 386		ac = &dev->audio_channels[ch];387		spin_lock_init(&ac->lock);388		ac->dev = dev;389		ac->ch = ch;390 391		err = tw686x_audio_dma_alloc(dev, ac);392		if (err < 0)393			goto err_cleanup;394	}395 396	err = tw686x_snd_pcm_init(dev);397	if (err < 0)398		goto err_cleanup;399 400	err = snd_card_register(card);401	if (!err)402		return 0;403 404err_cleanup:405	for (ch = 0; ch < max_channels(dev); ch++) {406		if (!dev->audio_channels[ch].dev)407			continue;408		tw686x_audio_dma_free(dev, &dev->audio_channels[ch]);409	}410	snd_card_free(card);411	dev->snd_card = NULL;412	return err;413}414