18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ALSA PCM device for the 48c2ecf20Sopenharmony_ci * ALSA interface to ivtv PCM capture streams 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2009,2012 Andy Walls <awalls@md.metrocast.net> 78c2ecf20Sopenharmony_ci * Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Portions of this work were sponsored by ONELAN Limited for the cx18 driver 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "ivtv-driver.h" 138c2ecf20Sopenharmony_ci#include "ivtv-queue.h" 148c2ecf20Sopenharmony_ci#include "ivtv-streams.h" 158c2ecf20Sopenharmony_ci#include "ivtv-fileops.h" 168c2ecf20Sopenharmony_ci#include "ivtv-alsa.h" 178c2ecf20Sopenharmony_ci#include "ivtv-alsa-pcm.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <sound/core.h> 208c2ecf20Sopenharmony_ci#include <sound/pcm.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic unsigned int pcm_debug; 248c2ecf20Sopenharmony_cimodule_param(pcm_debug, int, 0644); 258c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm"); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define dprintk(fmt, arg...) \ 288c2ecf20Sopenharmony_ci do { \ 298c2ecf20Sopenharmony_ci if (pcm_debug) \ 308c2ecf20Sopenharmony_ci pr_info("ivtv-alsa-pcm %s: " fmt, __func__, ##arg); \ 318c2ecf20Sopenharmony_ci } while (0) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_ivtv_hw_capture = { 348c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | 358c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP | 368c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 378c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID, 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_48000, 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci .rate_min = 48000, 448c2ecf20Sopenharmony_ci .rate_max = 48000, 458c2ecf20Sopenharmony_ci .channels_min = 2, 468c2ecf20Sopenharmony_ci .channels_max = 2, 478c2ecf20Sopenharmony_ci .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ 488c2ecf20Sopenharmony_ci .period_bytes_min = 64, /* 12544/2, */ 498c2ecf20Sopenharmony_ci .period_bytes_max = 12544, 508c2ecf20Sopenharmony_ci .periods_min = 2, 518c2ecf20Sopenharmony_ci .periods_max = 98, /* 12544, */ 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic void ivtv_alsa_announce_pcm_data(struct snd_ivtv_card *itvsc, 558c2ecf20Sopenharmony_ci u8 *pcm_data, 568c2ecf20Sopenharmony_ci size_t num_bytes) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 598c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 608c2ecf20Sopenharmony_ci unsigned int oldptr; 618c2ecf20Sopenharmony_ci unsigned int stride; 628c2ecf20Sopenharmony_ci int period_elapsed = 0; 638c2ecf20Sopenharmony_ci int length; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci dprintk("ivtv alsa announce ptr=%p data=%p num_bytes=%zu\n", itvsc, 668c2ecf20Sopenharmony_ci pcm_data, num_bytes); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci substream = itvsc->capture_pcm_substream; 698c2ecf20Sopenharmony_ci if (substream == NULL) { 708c2ecf20Sopenharmony_ci dprintk("substream was NULL\n"); 718c2ecf20Sopenharmony_ci return; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci runtime = substream->runtime; 758c2ecf20Sopenharmony_ci if (runtime == NULL) { 768c2ecf20Sopenharmony_ci dprintk("runtime was NULL\n"); 778c2ecf20Sopenharmony_ci return; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci stride = runtime->frame_bits >> 3; 818c2ecf20Sopenharmony_ci if (stride == 0) { 828c2ecf20Sopenharmony_ci dprintk("stride is zero\n"); 838c2ecf20Sopenharmony_ci return; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci length = num_bytes / stride; 878c2ecf20Sopenharmony_ci if (length == 0) { 888c2ecf20Sopenharmony_ci dprintk("%s: length was zero\n", __func__); 898c2ecf20Sopenharmony_ci return; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (runtime->dma_area == NULL) { 938c2ecf20Sopenharmony_ci dprintk("dma area was NULL - ignoring\n"); 948c2ecf20Sopenharmony_ci return; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci oldptr = itvsc->hwptr_done_capture; 988c2ecf20Sopenharmony_ci if (oldptr + length >= runtime->buffer_size) { 998c2ecf20Sopenharmony_ci unsigned int cnt = 1008c2ecf20Sopenharmony_ci runtime->buffer_size - oldptr; 1018c2ecf20Sopenharmony_ci memcpy(runtime->dma_area + oldptr * stride, pcm_data, 1028c2ecf20Sopenharmony_ci cnt * stride); 1038c2ecf20Sopenharmony_ci memcpy(runtime->dma_area, pcm_data + cnt * stride, 1048c2ecf20Sopenharmony_ci length * stride - cnt * stride); 1058c2ecf20Sopenharmony_ci } else { 1068c2ecf20Sopenharmony_ci memcpy(runtime->dma_area + oldptr * stride, pcm_data, 1078c2ecf20Sopenharmony_ci length * stride); 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci snd_pcm_stream_lock(substream); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci itvsc->hwptr_done_capture += length; 1128c2ecf20Sopenharmony_ci if (itvsc->hwptr_done_capture >= 1138c2ecf20Sopenharmony_ci runtime->buffer_size) 1148c2ecf20Sopenharmony_ci itvsc->hwptr_done_capture -= 1158c2ecf20Sopenharmony_ci runtime->buffer_size; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci itvsc->capture_transfer_done += length; 1188c2ecf20Sopenharmony_ci if (itvsc->capture_transfer_done >= 1198c2ecf20Sopenharmony_ci runtime->period_size) { 1208c2ecf20Sopenharmony_ci itvsc->capture_transfer_done -= 1218c2ecf20Sopenharmony_ci runtime->period_size; 1228c2ecf20Sopenharmony_ci period_elapsed = 1; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci snd_pcm_stream_unlock(substream); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (period_elapsed) 1288c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(substream); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int snd_ivtv_pcm_capture_open(struct snd_pcm_substream *substream) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream); 1348c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 1358c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev = itvsc->v4l2_dev; 1368c2ecf20Sopenharmony_ci struct ivtv *itv = to_ivtv(v4l2_dev); 1378c2ecf20Sopenharmony_ci struct ivtv_stream *s; 1388c2ecf20Sopenharmony_ci struct ivtv_open_id item; 1398c2ecf20Sopenharmony_ci int ret; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* Instruct the CX2341[56] to start sending packets */ 1428c2ecf20Sopenharmony_ci snd_ivtv_lock(itvsc); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (ivtv_init_on_first_open(itv)) { 1458c2ecf20Sopenharmony_ci snd_ivtv_unlock(itvsc); 1468c2ecf20Sopenharmony_ci return -ENXIO; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM]; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci v4l2_fh_init(&item.fh, &s->vdev); 1528c2ecf20Sopenharmony_ci item.itv = itv; 1538c2ecf20Sopenharmony_ci item.type = s->type; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* See if the stream is available */ 1568c2ecf20Sopenharmony_ci if (ivtv_claim_stream(&item, item.type)) { 1578c2ecf20Sopenharmony_ci /* No, it's already in use */ 1588c2ecf20Sopenharmony_ci v4l2_fh_exit(&item.fh); 1598c2ecf20Sopenharmony_ci snd_ivtv_unlock(itvsc); 1608c2ecf20Sopenharmony_ci return -EBUSY; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) || 1648c2ecf20Sopenharmony_ci test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) { 1658c2ecf20Sopenharmony_ci /* We're already streaming. No additional action required */ 1668c2ecf20Sopenharmony_ci snd_ivtv_unlock(itvsc); 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci runtime->hw = snd_ivtv_hw_capture; 1728c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 1738c2ecf20Sopenharmony_ci itvsc->capture_pcm_substream = substream; 1748c2ecf20Sopenharmony_ci runtime->private_data = itv; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci itv->pcm_announce_callback = ivtv_alsa_announce_pcm_data; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Not currently streaming, so start it up */ 1798c2ecf20Sopenharmony_ci set_bit(IVTV_F_S_STREAMING, &s->s_flags); 1808c2ecf20Sopenharmony_ci ret = ivtv_start_v4l2_encode_stream(s); 1818c2ecf20Sopenharmony_ci snd_ivtv_unlock(itvsc); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return ret; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int snd_ivtv_pcm_capture_close(struct snd_pcm_substream *substream) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream); 1898c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev = itvsc->v4l2_dev; 1908c2ecf20Sopenharmony_ci struct ivtv *itv = to_ivtv(v4l2_dev); 1918c2ecf20Sopenharmony_ci struct ivtv_stream *s; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* Instruct the ivtv to stop sending packets */ 1948c2ecf20Sopenharmony_ci snd_ivtv_lock(itvsc); 1958c2ecf20Sopenharmony_ci s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM]; 1968c2ecf20Sopenharmony_ci ivtv_stop_v4l2_encode_stream(s, 0); 1978c2ecf20Sopenharmony_ci clear_bit(IVTV_F_S_STREAMING, &s->s_flags); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci ivtv_release_stream(s); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci itv->pcm_announce_callback = NULL; 2028c2ecf20Sopenharmony_ci snd_ivtv_unlock(itvsc); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int snd_ivtv_pcm_prepare(struct snd_pcm_substream *substream) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci itvsc->hwptr_done_capture = 0; 2128c2ecf20Sopenharmony_ci itvsc->capture_transfer_done = 0; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return 0; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int snd_ivtv_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic 2238c2ecf20Sopenharmony_cisnd_pcm_uframes_t snd_ivtv_pcm_pointer(struct snd_pcm_substream *substream) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci unsigned long flags; 2268c2ecf20Sopenharmony_ci snd_pcm_uframes_t hwptr_done; 2278c2ecf20Sopenharmony_ci struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci spin_lock_irqsave(&itvsc->slock, flags); 2308c2ecf20Sopenharmony_ci hwptr_done = itvsc->hwptr_done_capture; 2318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&itvsc->slock, flags); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return hwptr_done; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_ivtv_pcm_capture_ops = { 2378c2ecf20Sopenharmony_ci .open = snd_ivtv_pcm_capture_open, 2388c2ecf20Sopenharmony_ci .close = snd_ivtv_pcm_capture_close, 2398c2ecf20Sopenharmony_ci .prepare = snd_ivtv_pcm_prepare, 2408c2ecf20Sopenharmony_ci .trigger = snd_ivtv_pcm_trigger, 2418c2ecf20Sopenharmony_ci .pointer = snd_ivtv_pcm_pointer, 2428c2ecf20Sopenharmony_ci}; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ciint snd_ivtv_pcm_create(struct snd_ivtv_card *itvsc) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct snd_pcm *sp; 2478c2ecf20Sopenharmony_ci struct snd_card *sc = itvsc->sc; 2488c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev = itvsc->v4l2_dev; 2498c2ecf20Sopenharmony_ci struct ivtv *itv = to_ivtv(v4l2_dev); 2508c2ecf20Sopenharmony_ci int ret; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci ret = snd_pcm_new(sc, "CX2341[56] PCM", 2538c2ecf20Sopenharmony_ci 0, /* PCM device 0, the only one for this card */ 2548c2ecf20Sopenharmony_ci 0, /* 0 playback substreams */ 2558c2ecf20Sopenharmony_ci 1, /* 1 capture substream */ 2568c2ecf20Sopenharmony_ci &sp); 2578c2ecf20Sopenharmony_ci if (ret) { 2588c2ecf20Sopenharmony_ci IVTV_ALSA_ERR("%s: snd_ivtv_pcm_create() failed with err %d\n", 2598c2ecf20Sopenharmony_ci __func__, ret); 2608c2ecf20Sopenharmony_ci goto err_exit; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci spin_lock_init(&itvsc->slock); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE, 2668c2ecf20Sopenharmony_ci &snd_ivtv_pcm_capture_ops); 2678c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(sp, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); 2688c2ecf20Sopenharmony_ci sp->info_flags = 0; 2698c2ecf20Sopenharmony_ci sp->private_data = itvsc; 2708c2ecf20Sopenharmony_ci strscpy(sp->name, itv->card_name, sizeof(sp->name)); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cierr_exit: 2758c2ecf20Sopenharmony_ci return ret; 2768c2ecf20Sopenharmony_ci} 277