18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// SH7760 ("camelot") DMABRG audio DMA unit support 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2007 Manuel Lauss <mano@roarinelk.homelinux.net> 68c2ecf20Sopenharmony_ci// 78c2ecf20Sopenharmony_ci// The SH7760 DMABRG provides 4 dma channels (2x rec, 2x play), which 88c2ecf20Sopenharmony_ci// trigger an interrupt when one half of the programmed transfer size 98c2ecf20Sopenharmony_ci// has been xmitted. 108c2ecf20Sopenharmony_ci// 118c2ecf20Sopenharmony_ci// FIXME: little-endian only for now 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/gfp.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 188c2ecf20Sopenharmony_ci#include <sound/core.h> 198c2ecf20Sopenharmony_ci#include <sound/pcm.h> 208c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 218c2ecf20Sopenharmony_ci#include <sound/soc.h> 228c2ecf20Sopenharmony_ci#include <asm/dmabrg.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* registers and bits */ 268c2ecf20Sopenharmony_ci#define BRGATXSAR 0x00 278c2ecf20Sopenharmony_ci#define BRGARXDAR 0x04 288c2ecf20Sopenharmony_ci#define BRGATXTCR 0x08 298c2ecf20Sopenharmony_ci#define BRGARXTCR 0x0C 308c2ecf20Sopenharmony_ci#define BRGACR 0x10 318c2ecf20Sopenharmony_ci#define BRGATXTCNT 0x14 328c2ecf20Sopenharmony_ci#define BRGARXTCNT 0x18 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define ACR_RAR (1 << 18) 358c2ecf20Sopenharmony_ci#define ACR_RDS (1 << 17) 368c2ecf20Sopenharmony_ci#define ACR_RDE (1 << 16) 378c2ecf20Sopenharmony_ci#define ACR_TAR (1 << 2) 388c2ecf20Sopenharmony_ci#define ACR_TDS (1 << 1) 398c2ecf20Sopenharmony_ci#define ACR_TDE (1 << 0) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* receiver/transmitter data alignment */ 428c2ecf20Sopenharmony_ci#define ACR_RAM_NONE (0 << 24) 438c2ecf20Sopenharmony_ci#define ACR_RAM_4BYTE (1 << 24) 448c2ecf20Sopenharmony_ci#define ACR_RAM_2WORD (2 << 24) 458c2ecf20Sopenharmony_ci#define ACR_TAM_NONE (0 << 8) 468c2ecf20Sopenharmony_ci#define ACR_TAM_4BYTE (1 << 8) 478c2ecf20Sopenharmony_ci#define ACR_TAM_2WORD (2 << 8) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct camelot_pcm { 518c2ecf20Sopenharmony_ci unsigned long mmio; /* DMABRG audio channel control reg MMIO */ 528c2ecf20Sopenharmony_ci unsigned int txid; /* ID of first DMABRG IRQ for this unit */ 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci struct snd_pcm_substream *tx_ss; 558c2ecf20Sopenharmony_ci unsigned long tx_period_size; 568c2ecf20Sopenharmony_ci unsigned int tx_period; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci struct snd_pcm_substream *rx_ss; 598c2ecf20Sopenharmony_ci unsigned long rx_period_size; 608c2ecf20Sopenharmony_ci unsigned int rx_period; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci} cam_pcm_data[2] = { 638c2ecf20Sopenharmony_ci { 648c2ecf20Sopenharmony_ci .mmio = 0xFE3C0040, 658c2ecf20Sopenharmony_ci .txid = DMABRGIRQ_A0TXF, 668c2ecf20Sopenharmony_ci }, 678c2ecf20Sopenharmony_ci { 688c2ecf20Sopenharmony_ci .mmio = 0xFE3C0060, 698c2ecf20Sopenharmony_ci .txid = DMABRGIRQ_A1TXF, 708c2ecf20Sopenharmony_ci }, 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define BRGREG(x) (*(unsigned long *)(cam->mmio + (x))) 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* 768c2ecf20Sopenharmony_ci * set a minimum of 16kb per period, to avoid interrupt-"storm" and 778c2ecf20Sopenharmony_ci * resulting skipping. In general, the bigger the minimum size, the 788c2ecf20Sopenharmony_ci * better for overall system performance. (The SH7760 is a puny CPU 798c2ecf20Sopenharmony_ci * with a slow SDRAM interface and poor internal bus bandwidth, 808c2ecf20Sopenharmony_ci * *especially* when the LCDC is active). The minimum for the DMAC 818c2ecf20Sopenharmony_ci * is 8 bytes; 16kbytes are enough to get skip-free playback of a 828c2ecf20Sopenharmony_ci * 44kHz/16bit/stereo MP3 on a lightly loaded system, and maintain 838c2ecf20Sopenharmony_ci * reasonable responsiveness in MPlayer. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_ci#define DMABRG_PERIOD_MIN 16 * 1024 868c2ecf20Sopenharmony_ci#define DMABRG_PERIOD_MAX 0x03fffffc 878c2ecf20Sopenharmony_ci#define DMABRG_PREALLOC_BUFFER 32 * 1024 888c2ecf20Sopenharmony_ci#define DMABRG_PREALLOC_BUFFER_MAX 32 * 1024 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware camelot_pcm_hardware = { 918c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | 928c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 938c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 948c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 958c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BATCH), 968c2ecf20Sopenharmony_ci .buffer_bytes_max = DMABRG_PERIOD_MAX, 978c2ecf20Sopenharmony_ci .period_bytes_min = DMABRG_PERIOD_MIN, 988c2ecf20Sopenharmony_ci .period_bytes_max = DMABRG_PERIOD_MAX / 2, 998c2ecf20Sopenharmony_ci .periods_min = 2, 1008c2ecf20Sopenharmony_ci .periods_max = 2, 1018c2ecf20Sopenharmony_ci .fifo_size = 128, 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic void camelot_txdma(void *data) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct camelot_pcm *cam = data; 1078c2ecf20Sopenharmony_ci cam->tx_period ^= 1; 1088c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(cam->tx_ss); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void camelot_rxdma(void *data) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct camelot_pcm *cam = data; 1148c2ecf20Sopenharmony_ci cam->rx_period ^= 1; 1158c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(cam->rx_ss); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int camelot_pcm_open(struct snd_soc_component *component, 1198c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1228c2ecf20Sopenharmony_ci struct camelot_pcm *cam = &cam_pcm_data[asoc_rtd_to_cpu(rtd, 0)->id]; 1238c2ecf20Sopenharmony_ci int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; 1248c2ecf20Sopenharmony_ci int ret, dmairq; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci snd_soc_set_runtime_hwparams(substream, &camelot_pcm_hardware); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* DMABRG buffer half/full events */ 1298c2ecf20Sopenharmony_ci dmairq = (recv) ? cam->txid + 2 : cam->txid; 1308c2ecf20Sopenharmony_ci if (recv) { 1318c2ecf20Sopenharmony_ci cam->rx_ss = substream; 1328c2ecf20Sopenharmony_ci ret = dmabrg_request_irq(dmairq, camelot_rxdma, cam); 1338c2ecf20Sopenharmony_ci if (unlikely(ret)) { 1348c2ecf20Sopenharmony_ci pr_debug("audio unit %d irqs already taken!\n", 1358c2ecf20Sopenharmony_ci asoc_rtd_to_cpu(rtd, 0)->id); 1368c2ecf20Sopenharmony_ci return -EBUSY; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci (void)dmabrg_request_irq(dmairq + 1,camelot_rxdma, cam); 1398c2ecf20Sopenharmony_ci } else { 1408c2ecf20Sopenharmony_ci cam->tx_ss = substream; 1418c2ecf20Sopenharmony_ci ret = dmabrg_request_irq(dmairq, camelot_txdma, cam); 1428c2ecf20Sopenharmony_ci if (unlikely(ret)) { 1438c2ecf20Sopenharmony_ci pr_debug("audio unit %d irqs already taken!\n", 1448c2ecf20Sopenharmony_ci asoc_rtd_to_cpu(rtd, 0)->id); 1458c2ecf20Sopenharmony_ci return -EBUSY; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci (void)dmabrg_request_irq(dmairq + 1, camelot_txdma, cam); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int camelot_pcm_close(struct snd_soc_component *component, 1538c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1568c2ecf20Sopenharmony_ci struct camelot_pcm *cam = &cam_pcm_data[asoc_rtd_to_cpu(rtd, 0)->id]; 1578c2ecf20Sopenharmony_ci int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; 1588c2ecf20Sopenharmony_ci int dmairq; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci dmairq = (recv) ? cam->txid + 2 : cam->txid; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (recv) 1638c2ecf20Sopenharmony_ci cam->rx_ss = NULL; 1648c2ecf20Sopenharmony_ci else 1658c2ecf20Sopenharmony_ci cam->tx_ss = NULL; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci dmabrg_free_irq(dmairq + 1); 1688c2ecf20Sopenharmony_ci dmabrg_free_irq(dmairq); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int camelot_hw_params(struct snd_soc_component *component, 1748c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 1758c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1788c2ecf20Sopenharmony_ci struct camelot_pcm *cam = &cam_pcm_data[asoc_rtd_to_cpu(rtd, 0)->id]; 1798c2ecf20Sopenharmony_ci int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; 1808c2ecf20Sopenharmony_ci int ret; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (recv) { 1838c2ecf20Sopenharmony_ci cam->rx_period_size = params_period_bytes(hw_params); 1848c2ecf20Sopenharmony_ci cam->rx_period = 0; 1858c2ecf20Sopenharmony_ci } else { 1868c2ecf20Sopenharmony_ci cam->tx_period_size = params_period_bytes(hw_params); 1878c2ecf20Sopenharmony_ci cam->tx_period = 0; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int camelot_prepare(struct snd_soc_component *component, 1938c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 1968c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1978c2ecf20Sopenharmony_ci struct camelot_pcm *cam = &cam_pcm_data[asoc_rtd_to_cpu(rtd, 0)->id]; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci pr_debug("PCM data: addr 0x%08lx len %d\n", 2008c2ecf20Sopenharmony_ci (u32)runtime->dma_addr, runtime->dma_bytes); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 2038c2ecf20Sopenharmony_ci BRGREG(BRGATXSAR) = (unsigned long)runtime->dma_area; 2048c2ecf20Sopenharmony_ci BRGREG(BRGATXTCR) = runtime->dma_bytes; 2058c2ecf20Sopenharmony_ci } else { 2068c2ecf20Sopenharmony_ci BRGREG(BRGARXDAR) = (unsigned long)runtime->dma_area; 2078c2ecf20Sopenharmony_ci BRGREG(BRGARXTCR) = runtime->dma_bytes; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic inline void dmabrg_play_dma_start(struct camelot_pcm *cam) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS); 2168c2ecf20Sopenharmony_ci /* start DMABRG engine: XFER start, auto-addr-reload */ 2178c2ecf20Sopenharmony_ci BRGREG(BRGACR) = acr | ACR_TDE | ACR_TAR | ACR_TAM_2WORD; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic inline void dmabrg_play_dma_stop(struct camelot_pcm *cam) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS); 2238c2ecf20Sopenharmony_ci /* forcibly terminate data transmission */ 2248c2ecf20Sopenharmony_ci BRGREG(BRGACR) = acr | ACR_TDS; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic inline void dmabrg_rec_dma_start(struct camelot_pcm *cam) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS); 2308c2ecf20Sopenharmony_ci /* start DMABRG engine: recv start, auto-reload */ 2318c2ecf20Sopenharmony_ci BRGREG(BRGACR) = acr | ACR_RDE | ACR_RAR | ACR_RAM_2WORD; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic inline void dmabrg_rec_dma_stop(struct camelot_pcm *cam) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS); 2378c2ecf20Sopenharmony_ci /* forcibly terminate data receiver */ 2388c2ecf20Sopenharmony_ci BRGREG(BRGACR) = acr | ACR_RDS; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int camelot_trigger(struct snd_soc_component *component, 2428c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, int cmd) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 2458c2ecf20Sopenharmony_ci struct camelot_pcm *cam = &cam_pcm_data[asoc_rtd_to_cpu(rtd, 0)->id]; 2468c2ecf20Sopenharmony_ci int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci switch (cmd) { 2498c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 2508c2ecf20Sopenharmony_ci if (recv) 2518c2ecf20Sopenharmony_ci dmabrg_rec_dma_start(cam); 2528c2ecf20Sopenharmony_ci else 2538c2ecf20Sopenharmony_ci dmabrg_play_dma_start(cam); 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 2568c2ecf20Sopenharmony_ci if (recv) 2578c2ecf20Sopenharmony_ci dmabrg_rec_dma_stop(cam); 2588c2ecf20Sopenharmony_ci else 2598c2ecf20Sopenharmony_ci dmabrg_play_dma_stop(cam); 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci default: 2628c2ecf20Sopenharmony_ci return -EINVAL; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t camelot_pos(struct snd_soc_component *component, 2698c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 2728c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 2738c2ecf20Sopenharmony_ci struct camelot_pcm *cam = &cam_pcm_data[asoc_rtd_to_cpu(rtd, 0)->id]; 2748c2ecf20Sopenharmony_ci int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; 2758c2ecf20Sopenharmony_ci unsigned long pos; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* cannot use the DMABRG pointer register: under load, by the 2788c2ecf20Sopenharmony_ci * time ALSA comes around to read the register, it is already 2798c2ecf20Sopenharmony_ci * far ahead (or worse, already done with the fragment) of the 2808c2ecf20Sopenharmony_ci * position at the time the IRQ was triggered, which results in 2818c2ecf20Sopenharmony_ci * fast-playback sound in my test application (ScummVM) 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci if (recv) 2848c2ecf20Sopenharmony_ci pos = cam->rx_period ? cam->rx_period_size : 0; 2858c2ecf20Sopenharmony_ci else 2868c2ecf20Sopenharmony_ci pos = cam->tx_period ? cam->tx_period_size : 0; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return bytes_to_frames(runtime, pos); 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int camelot_pcm_new(struct snd_soc_component *component, 2928c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct snd_pcm *pcm = rtd->pcm; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel 2978c2ecf20Sopenharmony_ci * in MMAP mode (i.e. aplay -M) 2988c2ecf20Sopenharmony_ci */ 2998c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, 3008c2ecf20Sopenharmony_ci SNDRV_DMA_TYPE_CONTINUOUS, 3018c2ecf20Sopenharmony_ci NULL, 3028c2ecf20Sopenharmony_ci DMABRG_PREALLOC_BUFFER, DMABRG_PREALLOC_BUFFER_MAX); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver sh7760_soc_component = { 3088c2ecf20Sopenharmony_ci .open = camelot_pcm_open, 3098c2ecf20Sopenharmony_ci .close = camelot_pcm_close, 3108c2ecf20Sopenharmony_ci .hw_params = camelot_hw_params, 3118c2ecf20Sopenharmony_ci .prepare = camelot_prepare, 3128c2ecf20Sopenharmony_ci .trigger = camelot_trigger, 3138c2ecf20Sopenharmony_ci .pointer = camelot_pos, 3148c2ecf20Sopenharmony_ci .pcm_construct = camelot_pcm_new, 3158c2ecf20Sopenharmony_ci}; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int sh7760_soc_platform_probe(struct platform_device *pdev) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci return devm_snd_soc_register_component(&pdev->dev, &sh7760_soc_component, 3208c2ecf20Sopenharmony_ci NULL, 0); 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic struct platform_driver sh7760_pcm_driver = { 3248c2ecf20Sopenharmony_ci .driver = { 3258c2ecf20Sopenharmony_ci .name = "sh7760-pcm-audio", 3268c2ecf20Sopenharmony_ci }, 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci .probe = sh7760_soc_platform_probe, 3298c2ecf20Sopenharmony_ci}; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cimodule_platform_driver(sh7760_pcm_driver); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3348c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SH7760 Audio DMA (DMABRG) driver"); 3358c2ecf20Sopenharmony_ciMODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>"); 336