18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// siu_dai.c - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral. 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> 68c2ecf20Sopenharmony_ci// Copyright (C) 2006 Carlos Munoz <carlos@kenati.com> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/firmware.h> 108c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <asm/clock.h> 158c2ecf20Sopenharmony_ci#include <asm/siu.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <sound/control.h> 188c2ecf20Sopenharmony_ci#include <sound/soc.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "siu.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* Board specifics */ 238c2ecf20Sopenharmony_ci#if defined(CONFIG_CPU_SUBTYPE_SH7722) 248c2ecf20Sopenharmony_ci# define SIU_MAX_VOLUME 0x1000 258c2ecf20Sopenharmony_ci#else 268c2ecf20Sopenharmony_ci# define SIU_MAX_VOLUME 0x7fff 278c2ecf20Sopenharmony_ci#endif 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define PRAM_SIZE 0x2000 308c2ecf20Sopenharmony_ci#define XRAM_SIZE 0x800 318c2ecf20Sopenharmony_ci#define YRAM_SIZE 0x800 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define XRAM_OFFSET 0x4000 348c2ecf20Sopenharmony_ci#define YRAM_OFFSET 0x6000 358c2ecf20Sopenharmony_ci#define REG_OFFSET 0xc000 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define PLAYBACK_ENABLED 1 388c2ecf20Sopenharmony_ci#define CAPTURE_ENABLED 2 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define VOLUME_CAPTURE 0 418c2ecf20Sopenharmony_ci#define VOLUME_PLAYBACK 1 428c2ecf20Sopenharmony_ci#define DFLT_VOLUME_LEVEL 0x08000800 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* 458c2ecf20Sopenharmony_ci * SPDIF is only available on port A and on some SIU implementations it is only 468c2ecf20Sopenharmony_ci * available for input. Due to the lack of hardware to test it, SPDIF is left 478c2ecf20Sopenharmony_ci * disabled in this driver version 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_cistruct format_flag { 508c2ecf20Sopenharmony_ci u32 i2s; 518c2ecf20Sopenharmony_ci u32 pcm; 528c2ecf20Sopenharmony_ci u32 spdif; 538c2ecf20Sopenharmony_ci u32 mask; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistruct port_flag { 578c2ecf20Sopenharmony_ci struct format_flag playback; 588c2ecf20Sopenharmony_ci struct format_flag capture; 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistruct siu_info *siu_i2s_data; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic struct port_flag siu_flags[SIU_PORT_NUM] = { 648c2ecf20Sopenharmony_ci [SIU_PORT_A] = { 658c2ecf20Sopenharmony_ci .playback = { 668c2ecf20Sopenharmony_ci .i2s = 0x50000000, 678c2ecf20Sopenharmony_ci .pcm = 0x40000000, 688c2ecf20Sopenharmony_ci .spdif = 0x80000000, /* not on all SIU versions */ 698c2ecf20Sopenharmony_ci .mask = 0xd0000000, 708c2ecf20Sopenharmony_ci }, 718c2ecf20Sopenharmony_ci .capture = { 728c2ecf20Sopenharmony_ci .i2s = 0x05000000, 738c2ecf20Sopenharmony_ci .pcm = 0x04000000, 748c2ecf20Sopenharmony_ci .spdif = 0x08000000, 758c2ecf20Sopenharmony_ci .mask = 0x0d000000, 768c2ecf20Sopenharmony_ci }, 778c2ecf20Sopenharmony_ci }, 788c2ecf20Sopenharmony_ci [SIU_PORT_B] = { 798c2ecf20Sopenharmony_ci .playback = { 808c2ecf20Sopenharmony_ci .i2s = 0x00500000, 818c2ecf20Sopenharmony_ci .pcm = 0x00400000, 828c2ecf20Sopenharmony_ci .spdif = 0, /* impossible - turn off */ 838c2ecf20Sopenharmony_ci .mask = 0x00500000, 848c2ecf20Sopenharmony_ci }, 858c2ecf20Sopenharmony_ci .capture = { 868c2ecf20Sopenharmony_ci .i2s = 0x00050000, 878c2ecf20Sopenharmony_ci .pcm = 0x00040000, 888c2ecf20Sopenharmony_ci .spdif = 0, /* impossible - turn off */ 898c2ecf20Sopenharmony_ci .mask = 0x00050000, 908c2ecf20Sopenharmony_ci }, 918c2ecf20Sopenharmony_ci }, 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void siu_dai_start(struct siu_port *port_info) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct siu_info *info = siu_i2s_data; 978c2ecf20Sopenharmony_ci u32 __iomem *base = info->reg; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci dev_dbg(port_info->pcm->card->dev, "%s\n", __func__); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* Issue software reset to siu */ 1028c2ecf20Sopenharmony_ci siu_write32(base + SIU_SRCTL, 0); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* Wait for the reset to take effect */ 1058c2ecf20Sopenharmony_ci udelay(1); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci port_info->stfifo = 0; 1088c2ecf20Sopenharmony_ci port_info->trdat = 0; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* portA, portB, SIU operate */ 1118c2ecf20Sopenharmony_ci siu_write32(base + SIU_SRCTL, 0x301); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* portA=256fs, portB=256fs */ 1148c2ecf20Sopenharmony_ci siu_write32(base + SIU_CKCTL, 0x40400000); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* portA's BRG does not divide SIUCKA */ 1178c2ecf20Sopenharmony_ci siu_write32(base + SIU_BRGASEL, 0); 1188c2ecf20Sopenharmony_ci siu_write32(base + SIU_BRRA, 0); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* portB's BRG divides SIUCKB by half */ 1218c2ecf20Sopenharmony_ci siu_write32(base + SIU_BRGBSEL, 1); 1228c2ecf20Sopenharmony_ci siu_write32(base + SIU_BRRB, 0); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci siu_write32(base + SIU_IFCTL, 0x44440000); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* portA: 32 bit/fs, master; portB: 32 bit/fs, master */ 1278c2ecf20Sopenharmony_ci siu_write32(base + SIU_SFORM, 0x0c0c0000); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* 1308c2ecf20Sopenharmony_ci * Volume levels: looks like the DSP firmware implements volume controls 1318c2ecf20Sopenharmony_ci * differently from what's described in the datasheet 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci siu_write32(base + SIU_SBDVCA, port_info->playback.volume); 1348c2ecf20Sopenharmony_ci siu_write32(base + SIU_SBDVCB, port_info->capture.volume); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void siu_dai_stop(struct siu_port *port_info) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct siu_info *info = siu_i2s_data; 1408c2ecf20Sopenharmony_ci u32 __iomem *base = info->reg; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* SIU software reset */ 1438c2ecf20Sopenharmony_ci siu_write32(base + SIU_SRCTL, 0); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic void siu_dai_spbAselect(struct siu_port *port_info) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct siu_info *info = siu_i2s_data; 1498c2ecf20Sopenharmony_ci struct siu_firmware *fw = &info->fw; 1508c2ecf20Sopenharmony_ci u32 *ydef = fw->yram0; 1518c2ecf20Sopenharmony_ci u32 idx; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* path A use */ 1548c2ecf20Sopenharmony_ci if (!info->port_id) 1558c2ecf20Sopenharmony_ci idx = 1; /* portA */ 1568c2ecf20Sopenharmony_ci else 1578c2ecf20Sopenharmony_ci idx = 2; /* portB */ 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci ydef[0] = (fw->spbpar[idx].ab1a << 16) | 1608c2ecf20Sopenharmony_ci (fw->spbpar[idx].ab0a << 8) | 1618c2ecf20Sopenharmony_ci (fw->spbpar[idx].dir << 7) | 3; 1628c2ecf20Sopenharmony_ci ydef[1] = fw->yram0[1]; /* 0x03000300 */ 1638c2ecf20Sopenharmony_ci ydef[2] = (16 / 2) << 24; 1648c2ecf20Sopenharmony_ci ydef[3] = fw->yram0[3]; /* 0 */ 1658c2ecf20Sopenharmony_ci ydef[4] = fw->yram0[4]; /* 0 */ 1668c2ecf20Sopenharmony_ci ydef[7] = fw->spbpar[idx].event; 1678c2ecf20Sopenharmony_ci port_info->stfifo |= fw->spbpar[idx].stfifo; 1688c2ecf20Sopenharmony_ci port_info->trdat |= fw->spbpar[idx].trdat; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic void siu_dai_spbBselect(struct siu_port *port_info) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct siu_info *info = siu_i2s_data; 1748c2ecf20Sopenharmony_ci struct siu_firmware *fw = &info->fw; 1758c2ecf20Sopenharmony_ci u32 *ydef = fw->yram0; 1768c2ecf20Sopenharmony_ci u32 idx; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* path B use */ 1798c2ecf20Sopenharmony_ci if (!info->port_id) 1808c2ecf20Sopenharmony_ci idx = 7; /* portA */ 1818c2ecf20Sopenharmony_ci else 1828c2ecf20Sopenharmony_ci idx = 8; /* portB */ 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci ydef[5] = (fw->spbpar[idx].ab1a << 16) | 1858c2ecf20Sopenharmony_ci (fw->spbpar[idx].ab0a << 8) | 1; 1868c2ecf20Sopenharmony_ci ydef[6] = fw->spbpar[idx].event; 1878c2ecf20Sopenharmony_ci port_info->stfifo |= fw->spbpar[idx].stfifo; 1888c2ecf20Sopenharmony_ci port_info->trdat |= fw->spbpar[idx].trdat; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void siu_dai_open(struct siu_stream *siu_stream) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct siu_info *info = siu_i2s_data; 1948c2ecf20Sopenharmony_ci u32 __iomem *base = info->reg; 1958c2ecf20Sopenharmony_ci u32 srctl, ifctl; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci srctl = siu_read32(base + SIU_SRCTL); 1988c2ecf20Sopenharmony_ci ifctl = siu_read32(base + SIU_IFCTL); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci switch (info->port_id) { 2018c2ecf20Sopenharmony_ci case SIU_PORT_A: 2028c2ecf20Sopenharmony_ci /* portA operates */ 2038c2ecf20Sopenharmony_ci srctl |= 0x200; 2048c2ecf20Sopenharmony_ci ifctl &= ~0xc2; 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci case SIU_PORT_B: 2078c2ecf20Sopenharmony_ci /* portB operates */ 2088c2ecf20Sopenharmony_ci srctl |= 0x100; 2098c2ecf20Sopenharmony_ci ifctl &= ~0x31; 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci siu_write32(base + SIU_SRCTL, srctl); 2148c2ecf20Sopenharmony_ci /* Unmute and configure portA */ 2158c2ecf20Sopenharmony_ci siu_write32(base + SIU_IFCTL, ifctl); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* 2198c2ecf20Sopenharmony_ci * At the moment only fixed Left-upper, Left-lower, Right-upper, Right-lower 2208c2ecf20Sopenharmony_ci * packing is supported 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_cistatic void siu_dai_pcmdatapack(struct siu_stream *siu_stream) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct siu_info *info = siu_i2s_data; 2258c2ecf20Sopenharmony_ci u32 __iomem *base = info->reg; 2268c2ecf20Sopenharmony_ci u32 dpak; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci dpak = siu_read32(base + SIU_DPAK); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci switch (info->port_id) { 2318c2ecf20Sopenharmony_ci case SIU_PORT_A: 2328c2ecf20Sopenharmony_ci dpak &= ~0xc0000000; 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci case SIU_PORT_B: 2358c2ecf20Sopenharmony_ci dpak &= ~0x00c00000; 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci siu_write32(base + SIU_DPAK, dpak); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int siu_dai_spbstart(struct siu_port *port_info) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct siu_info *info = siu_i2s_data; 2458c2ecf20Sopenharmony_ci u32 __iomem *base = info->reg; 2468c2ecf20Sopenharmony_ci struct siu_firmware *fw = &info->fw; 2478c2ecf20Sopenharmony_ci u32 *ydef = fw->yram0; 2488c2ecf20Sopenharmony_ci int cnt; 2498c2ecf20Sopenharmony_ci u32 __iomem *add; 2508c2ecf20Sopenharmony_ci u32 *ptr; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* Load SPB Program in PRAM */ 2538c2ecf20Sopenharmony_ci ptr = fw->pram0; 2548c2ecf20Sopenharmony_ci add = info->pram; 2558c2ecf20Sopenharmony_ci for (cnt = 0; cnt < PRAM0_SIZE; cnt++, add++, ptr++) 2568c2ecf20Sopenharmony_ci siu_write32(add, *ptr); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci ptr = fw->pram1; 2598c2ecf20Sopenharmony_ci add = info->pram + (0x0100 / sizeof(u32)); 2608c2ecf20Sopenharmony_ci for (cnt = 0; cnt < PRAM1_SIZE; cnt++, add++, ptr++) 2618c2ecf20Sopenharmony_ci siu_write32(add, *ptr); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* XRAM initialization */ 2648c2ecf20Sopenharmony_ci add = info->xram; 2658c2ecf20Sopenharmony_ci for (cnt = 0; cnt < XRAM0_SIZE + XRAM1_SIZE + XRAM2_SIZE; cnt++, add++) 2668c2ecf20Sopenharmony_ci siu_write32(add, 0); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* YRAM variable area initialization */ 2698c2ecf20Sopenharmony_ci add = info->yram; 2708c2ecf20Sopenharmony_ci for (cnt = 0; cnt < YRAM_DEF_SIZE; cnt++, add++) 2718c2ecf20Sopenharmony_ci siu_write32(add, ydef[cnt]); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* YRAM FIR coefficient area initialization */ 2748c2ecf20Sopenharmony_ci add = info->yram + (0x0200 / sizeof(u32)); 2758c2ecf20Sopenharmony_ci for (cnt = 0; cnt < YRAM_FIR_SIZE; cnt++, add++) 2768c2ecf20Sopenharmony_ci siu_write32(add, fw->yram_fir_coeff[cnt]); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* YRAM IIR coefficient area initialization */ 2798c2ecf20Sopenharmony_ci add = info->yram + (0x0600 / sizeof(u32)); 2808c2ecf20Sopenharmony_ci for (cnt = 0; cnt < YRAM_IIR_SIZE; cnt++, add++) 2818c2ecf20Sopenharmony_ci siu_write32(add, 0); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci siu_write32(base + SIU_TRDAT, port_info->trdat); 2848c2ecf20Sopenharmony_ci port_info->trdat = 0x0; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* SPB start condition: software */ 2888c2ecf20Sopenharmony_ci siu_write32(base + SIU_SBACTIV, 0); 2898c2ecf20Sopenharmony_ci /* Start SPB */ 2908c2ecf20Sopenharmony_ci siu_write32(base + SIU_SBCTL, 0xc0000000); 2918c2ecf20Sopenharmony_ci /* Wait for program to halt */ 2928c2ecf20Sopenharmony_ci cnt = 0x10000; 2938c2ecf20Sopenharmony_ci while (--cnt && siu_read32(base + SIU_SBCTL) != 0x80000000) 2948c2ecf20Sopenharmony_ci cpu_relax(); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (!cnt) 2978c2ecf20Sopenharmony_ci return -EBUSY; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* SPB program start address setting */ 3008c2ecf20Sopenharmony_ci siu_write32(base + SIU_SBPSET, 0x00400000); 3018c2ecf20Sopenharmony_ci /* SPB hardware start(FIFOCTL source) */ 3028c2ecf20Sopenharmony_ci siu_write32(base + SIU_SBACTIV, 0xc0000000); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic void siu_dai_spbstop(struct siu_port *port_info) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct siu_info *info = siu_i2s_data; 3108c2ecf20Sopenharmony_ci u32 __iomem *base = info->reg; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci siu_write32(base + SIU_SBACTIV, 0); 3138c2ecf20Sopenharmony_ci /* SPB stop */ 3148c2ecf20Sopenharmony_ci siu_write32(base + SIU_SBCTL, 0); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci port_info->stfifo = 0; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/* API functions */ 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci/* Playback and capture hardware properties are identical */ 3228c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware siu_dai_pcm_hw = { 3238c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_INTERLEAVED, 3248c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16, 3258c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 3268c2ecf20Sopenharmony_ci .rate_min = 8000, 3278c2ecf20Sopenharmony_ci .rate_max = 48000, 3288c2ecf20Sopenharmony_ci .channels_min = 2, 3298c2ecf20Sopenharmony_ci .channels_max = 2, 3308c2ecf20Sopenharmony_ci .buffer_bytes_max = SIU_BUFFER_BYTES_MAX, 3318c2ecf20Sopenharmony_ci .period_bytes_min = SIU_PERIOD_BYTES_MIN, 3328c2ecf20Sopenharmony_ci .period_bytes_max = SIU_PERIOD_BYTES_MAX, 3338c2ecf20Sopenharmony_ci .periods_min = SIU_PERIODS_MIN, 3348c2ecf20Sopenharmony_ci .periods_max = SIU_PERIODS_MAX, 3358c2ecf20Sopenharmony_ci}; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic int siu_dai_info_volume(struct snd_kcontrol *kctrl, 3388c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct siu_port *port_info = snd_kcontrol_chip(kctrl); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci dev_dbg(port_info->pcm->card->dev, "%s\n", __func__); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 3458c2ecf20Sopenharmony_ci uinfo->count = 2; 3468c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 3478c2ecf20Sopenharmony_ci uinfo->value.integer.max = SIU_MAX_VOLUME; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci return 0; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic int siu_dai_get_volume(struct snd_kcontrol *kctrl, 3538c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct siu_port *port_info = snd_kcontrol_chip(kctrl); 3568c2ecf20Sopenharmony_ci struct device *dev = port_info->pcm->card->dev; 3578c2ecf20Sopenharmony_ci u32 vol; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci dev_dbg(dev, "%s\n", __func__); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci switch (kctrl->private_value) { 3628c2ecf20Sopenharmony_ci case VOLUME_PLAYBACK: 3638c2ecf20Sopenharmony_ci /* Playback is always on port 0 */ 3648c2ecf20Sopenharmony_ci vol = port_info->playback.volume; 3658c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = vol & 0xffff; 3668c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = vol >> 16 & 0xffff; 3678c2ecf20Sopenharmony_ci break; 3688c2ecf20Sopenharmony_ci case VOLUME_CAPTURE: 3698c2ecf20Sopenharmony_ci /* Capture is always on port 1 */ 3708c2ecf20Sopenharmony_ci vol = port_info->capture.volume; 3718c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = vol & 0xffff; 3728c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = vol >> 16 & 0xffff; 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci default: 3758c2ecf20Sopenharmony_ci dev_err(dev, "%s() invalid private_value=%ld\n", 3768c2ecf20Sopenharmony_ci __func__, kctrl->private_value); 3778c2ecf20Sopenharmony_ci return -EINVAL; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return 0; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int siu_dai_put_volume(struct snd_kcontrol *kctrl, 3848c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct siu_port *port_info = snd_kcontrol_chip(kctrl); 3878c2ecf20Sopenharmony_ci struct device *dev = port_info->pcm->card->dev; 3888c2ecf20Sopenharmony_ci struct siu_info *info = siu_i2s_data; 3898c2ecf20Sopenharmony_ci u32 __iomem *base = info->reg; 3908c2ecf20Sopenharmony_ci u32 new_vol; 3918c2ecf20Sopenharmony_ci u32 cur_vol; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci dev_dbg(dev, "%s\n", __func__); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (ucontrol->value.integer.value[0] < 0 || 3968c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] > SIU_MAX_VOLUME || 3978c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] < 0 || 3988c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] > SIU_MAX_VOLUME) 3998c2ecf20Sopenharmony_ci return -EINVAL; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci new_vol = ucontrol->value.integer.value[0] | 4028c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] << 16; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* See comment above - DSP firmware implementation */ 4058c2ecf20Sopenharmony_ci switch (kctrl->private_value) { 4068c2ecf20Sopenharmony_ci case VOLUME_PLAYBACK: 4078c2ecf20Sopenharmony_ci /* Playback is always on port 0 */ 4088c2ecf20Sopenharmony_ci cur_vol = port_info->playback.volume; 4098c2ecf20Sopenharmony_ci siu_write32(base + SIU_SBDVCA, new_vol); 4108c2ecf20Sopenharmony_ci port_info->playback.volume = new_vol; 4118c2ecf20Sopenharmony_ci break; 4128c2ecf20Sopenharmony_ci case VOLUME_CAPTURE: 4138c2ecf20Sopenharmony_ci /* Capture is always on port 1 */ 4148c2ecf20Sopenharmony_ci cur_vol = port_info->capture.volume; 4158c2ecf20Sopenharmony_ci siu_write32(base + SIU_SBDVCB, new_vol); 4168c2ecf20Sopenharmony_ci port_info->capture.volume = new_vol; 4178c2ecf20Sopenharmony_ci break; 4188c2ecf20Sopenharmony_ci default: 4198c2ecf20Sopenharmony_ci dev_err(dev, "%s() invalid private_value=%ld\n", 4208c2ecf20Sopenharmony_ci __func__, kctrl->private_value); 4218c2ecf20Sopenharmony_ci return -EINVAL; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (cur_vol != new_vol) 4258c2ecf20Sopenharmony_ci return 1; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci return 0; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new playback_controls = { 4318c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4328c2ecf20Sopenharmony_ci .name = "PCM Playback Volume", 4338c2ecf20Sopenharmony_ci .index = 0, 4348c2ecf20Sopenharmony_ci .info = siu_dai_info_volume, 4358c2ecf20Sopenharmony_ci .get = siu_dai_get_volume, 4368c2ecf20Sopenharmony_ci .put = siu_dai_put_volume, 4378c2ecf20Sopenharmony_ci .private_value = VOLUME_PLAYBACK, 4388c2ecf20Sopenharmony_ci}; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new capture_controls = { 4418c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4428c2ecf20Sopenharmony_ci .name = "PCM Capture Volume", 4438c2ecf20Sopenharmony_ci .index = 0, 4448c2ecf20Sopenharmony_ci .info = siu_dai_info_volume, 4458c2ecf20Sopenharmony_ci .get = siu_dai_get_volume, 4468c2ecf20Sopenharmony_ci .put = siu_dai_put_volume, 4478c2ecf20Sopenharmony_ci .private_value = VOLUME_CAPTURE, 4488c2ecf20Sopenharmony_ci}; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ciint siu_init_port(int port, struct siu_port **port_info, struct snd_card *card) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct device *dev = card->dev; 4538c2ecf20Sopenharmony_ci struct snd_kcontrol *kctrl; 4548c2ecf20Sopenharmony_ci int ret; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci *port_info = kzalloc(sizeof(**port_info), GFP_KERNEL); 4578c2ecf20Sopenharmony_ci if (!*port_info) 4588c2ecf20Sopenharmony_ci return -ENOMEM; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: port #%d@%p\n", __func__, port, *port_info); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci (*port_info)->playback.volume = DFLT_VOLUME_LEVEL; 4638c2ecf20Sopenharmony_ci (*port_info)->capture.volume = DFLT_VOLUME_LEVEL; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* 4668c2ecf20Sopenharmony_ci * Add mixer support. The SPB is used to change the volume. Both 4678c2ecf20Sopenharmony_ci * ports use the same SPB. Therefore, we only register one 4688c2ecf20Sopenharmony_ci * control instance since it will be used by both channels. 4698c2ecf20Sopenharmony_ci * In error case we continue without controls. 4708c2ecf20Sopenharmony_ci */ 4718c2ecf20Sopenharmony_ci kctrl = snd_ctl_new1(&playback_controls, *port_info); 4728c2ecf20Sopenharmony_ci ret = snd_ctl_add(card, kctrl); 4738c2ecf20Sopenharmony_ci if (ret < 0) 4748c2ecf20Sopenharmony_ci dev_err(dev, 4758c2ecf20Sopenharmony_ci "failed to add playback controls %p port=%d err=%d\n", 4768c2ecf20Sopenharmony_ci kctrl, port, ret); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci kctrl = snd_ctl_new1(&capture_controls, *port_info); 4798c2ecf20Sopenharmony_ci ret = snd_ctl_add(card, kctrl); 4808c2ecf20Sopenharmony_ci if (ret < 0) 4818c2ecf20Sopenharmony_ci dev_err(dev, 4828c2ecf20Sopenharmony_ci "failed to add capture controls %p port=%d err=%d\n", 4838c2ecf20Sopenharmony_ci kctrl, port, ret); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci return 0; 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_civoid siu_free_port(struct siu_port *port_info) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci kfree(port_info); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic int siu_dai_startup(struct snd_pcm_substream *substream, 4948c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci struct siu_info *info = snd_soc_dai_get_drvdata(dai); 4978c2ecf20Sopenharmony_ci struct snd_pcm_runtime *rt = substream->runtime; 4988c2ecf20Sopenharmony_ci struct siu_port *port_info = siu_port_info(substream); 4998c2ecf20Sopenharmony_ci int ret; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__, 5028c2ecf20Sopenharmony_ci info->port_id, port_info); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci snd_soc_set_runtime_hwparams(substream, &siu_dai_pcm_hw); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); 5078c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 5088c2ecf20Sopenharmony_ci return ret; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci siu_dai_start(port_info); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci return 0; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic void siu_dai_shutdown(struct snd_pcm_substream *substream, 5168c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci struct siu_info *info = snd_soc_dai_get_drvdata(dai); 5198c2ecf20Sopenharmony_ci struct siu_port *port_info = siu_port_info(substream); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__, 5228c2ecf20Sopenharmony_ci info->port_id, port_info); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 5258c2ecf20Sopenharmony_ci port_info->play_cap &= ~PLAYBACK_ENABLED; 5268c2ecf20Sopenharmony_ci else 5278c2ecf20Sopenharmony_ci port_info->play_cap &= ~CAPTURE_ENABLED; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci /* Stop the siu if the other stream is not using it */ 5308c2ecf20Sopenharmony_ci if (!port_info->play_cap) { 5318c2ecf20Sopenharmony_ci /* during stmread or stmwrite ? */ 5328c2ecf20Sopenharmony_ci if (WARN_ON(port_info->playback.rw_flg || port_info->capture.rw_flg)) 5338c2ecf20Sopenharmony_ci return; 5348c2ecf20Sopenharmony_ci siu_dai_spbstop(port_info); 5358c2ecf20Sopenharmony_ci siu_dai_stop(port_info); 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci/* PCM part of siu_dai_playback_prepare() / siu_dai_capture_prepare() */ 5408c2ecf20Sopenharmony_cistatic int siu_dai_prepare(struct snd_pcm_substream *substream, 5418c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci struct siu_info *info = snd_soc_dai_get_drvdata(dai); 5448c2ecf20Sopenharmony_ci struct snd_pcm_runtime *rt = substream->runtime; 5458c2ecf20Sopenharmony_ci struct siu_port *port_info = siu_port_info(substream); 5468c2ecf20Sopenharmony_ci struct siu_stream *siu_stream; 5478c2ecf20Sopenharmony_ci int self, ret; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci dev_dbg(substream->pcm->card->dev, 5508c2ecf20Sopenharmony_ci "%s: port %d, active streams %lx, %d channels\n", 5518c2ecf20Sopenharmony_ci __func__, info->port_id, port_info->play_cap, rt->channels); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 5548c2ecf20Sopenharmony_ci self = PLAYBACK_ENABLED; 5558c2ecf20Sopenharmony_ci siu_stream = &port_info->playback; 5568c2ecf20Sopenharmony_ci } else { 5578c2ecf20Sopenharmony_ci self = CAPTURE_ENABLED; 5588c2ecf20Sopenharmony_ci siu_stream = &port_info->capture; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* Set up the siu if not already done */ 5628c2ecf20Sopenharmony_ci if (!port_info->play_cap) { 5638c2ecf20Sopenharmony_ci siu_stream->rw_flg = 0; /* stream-data transfer flag */ 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci siu_dai_spbAselect(port_info); 5668c2ecf20Sopenharmony_ci siu_dai_spbBselect(port_info); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci siu_dai_open(siu_stream); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci siu_dai_pcmdatapack(siu_stream); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci ret = siu_dai_spbstart(port_info); 5738c2ecf20Sopenharmony_ci if (ret < 0) 5748c2ecf20Sopenharmony_ci goto fail; 5758c2ecf20Sopenharmony_ci } else { 5768c2ecf20Sopenharmony_ci ret = 0; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci port_info->play_cap |= self; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cifail: 5828c2ecf20Sopenharmony_ci return ret; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci/* 5868c2ecf20Sopenharmony_ci * SIU can set bus format to I2S / PCM / SPDIF independently for playback and 5878c2ecf20Sopenharmony_ci * capture, however, the current API sets the bus format globally for a DAI. 5888c2ecf20Sopenharmony_ci */ 5898c2ecf20Sopenharmony_cistatic int siu_dai_set_fmt(struct snd_soc_dai *dai, 5908c2ecf20Sopenharmony_ci unsigned int fmt) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci struct siu_info *info = snd_soc_dai_get_drvdata(dai); 5938c2ecf20Sopenharmony_ci u32 __iomem *base = info->reg; 5948c2ecf20Sopenharmony_ci u32 ifctl; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: fmt 0x%x on port %d\n", 5978c2ecf20Sopenharmony_ci __func__, fmt, info->port_id); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (info->port_id < 0) 6008c2ecf20Sopenharmony_ci return -ENODEV; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* Here select between I2S / PCM / SPDIF */ 6038c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 6048c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 6058c2ecf20Sopenharmony_ci ifctl = siu_flags[info->port_id].playback.i2s | 6068c2ecf20Sopenharmony_ci siu_flags[info->port_id].capture.i2s; 6078c2ecf20Sopenharmony_ci break; 6088c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 6098c2ecf20Sopenharmony_ci ifctl = siu_flags[info->port_id].playback.pcm | 6108c2ecf20Sopenharmony_ci siu_flags[info->port_id].capture.pcm; 6118c2ecf20Sopenharmony_ci break; 6128c2ecf20Sopenharmony_ci /* SPDIF disabled - see comment at the top */ 6138c2ecf20Sopenharmony_ci default: 6148c2ecf20Sopenharmony_ci return -EINVAL; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci ifctl |= ~(siu_flags[info->port_id].playback.mask | 6188c2ecf20Sopenharmony_ci siu_flags[info->port_id].capture.mask) & 6198c2ecf20Sopenharmony_ci siu_read32(base + SIU_IFCTL); 6208c2ecf20Sopenharmony_ci siu_write32(base + SIU_IFCTL, ifctl); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci return 0; 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic int siu_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, 6268c2ecf20Sopenharmony_ci unsigned int freq, int dir) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci struct clk *siu_clk, *parent_clk; 6298c2ecf20Sopenharmony_ci char *siu_name, *parent_name; 6308c2ecf20Sopenharmony_ci int ret; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (dir != SND_SOC_CLOCK_IN) 6338c2ecf20Sopenharmony_ci return -EINVAL; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: using clock %d\n", __func__, clk_id); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci switch (clk_id) { 6388c2ecf20Sopenharmony_ci case SIU_CLKA_PLL: 6398c2ecf20Sopenharmony_ci siu_name = "siua_clk"; 6408c2ecf20Sopenharmony_ci parent_name = "pll_clk"; 6418c2ecf20Sopenharmony_ci break; 6428c2ecf20Sopenharmony_ci case SIU_CLKA_EXT: 6438c2ecf20Sopenharmony_ci siu_name = "siua_clk"; 6448c2ecf20Sopenharmony_ci parent_name = "siumcka_clk"; 6458c2ecf20Sopenharmony_ci break; 6468c2ecf20Sopenharmony_ci case SIU_CLKB_PLL: 6478c2ecf20Sopenharmony_ci siu_name = "siub_clk"; 6488c2ecf20Sopenharmony_ci parent_name = "pll_clk"; 6498c2ecf20Sopenharmony_ci break; 6508c2ecf20Sopenharmony_ci case SIU_CLKB_EXT: 6518c2ecf20Sopenharmony_ci siu_name = "siub_clk"; 6528c2ecf20Sopenharmony_ci parent_name = "siumckb_clk"; 6538c2ecf20Sopenharmony_ci break; 6548c2ecf20Sopenharmony_ci default: 6558c2ecf20Sopenharmony_ci return -EINVAL; 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci siu_clk = clk_get(dai->dev, siu_name); 6598c2ecf20Sopenharmony_ci if (IS_ERR(siu_clk)) { 6608c2ecf20Sopenharmony_ci dev_err(dai->dev, "%s: cannot get a SIU clock: %ld\n", __func__, 6618c2ecf20Sopenharmony_ci PTR_ERR(siu_clk)); 6628c2ecf20Sopenharmony_ci return PTR_ERR(siu_clk); 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci parent_clk = clk_get(dai->dev, parent_name); 6668c2ecf20Sopenharmony_ci if (IS_ERR(parent_clk)) { 6678c2ecf20Sopenharmony_ci ret = PTR_ERR(parent_clk); 6688c2ecf20Sopenharmony_ci dev_err(dai->dev, "cannot get a SIU clock parent: %d\n", ret); 6698c2ecf20Sopenharmony_ci goto epclkget; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci ret = clk_set_parent(siu_clk, parent_clk); 6738c2ecf20Sopenharmony_ci if (ret < 0) { 6748c2ecf20Sopenharmony_ci dev_err(dai->dev, "cannot reparent the SIU clock: %d\n", ret); 6758c2ecf20Sopenharmony_ci goto eclksetp; 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci ret = clk_set_rate(siu_clk, freq); 6798c2ecf20Sopenharmony_ci if (ret < 0) 6808c2ecf20Sopenharmony_ci dev_err(dai->dev, "cannot set SIU clock rate: %d\n", ret); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* TODO: when clkdev gets reference counting we'll move these to siu_dai_shutdown() */ 6838c2ecf20Sopenharmony_cieclksetp: 6848c2ecf20Sopenharmony_ci clk_put(parent_clk); 6858c2ecf20Sopenharmony_ciepclkget: 6868c2ecf20Sopenharmony_ci clk_put(siu_clk); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci return ret; 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops siu_dai_ops = { 6928c2ecf20Sopenharmony_ci .startup = siu_dai_startup, 6938c2ecf20Sopenharmony_ci .shutdown = siu_dai_shutdown, 6948c2ecf20Sopenharmony_ci .prepare = siu_dai_prepare, 6958c2ecf20Sopenharmony_ci .set_sysclk = siu_dai_set_sysclk, 6968c2ecf20Sopenharmony_ci .set_fmt = siu_dai_set_fmt, 6978c2ecf20Sopenharmony_ci}; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver siu_i2s_dai = { 7008c2ecf20Sopenharmony_ci .name = "siu-i2s-dai", 7018c2ecf20Sopenharmony_ci .playback = { 7028c2ecf20Sopenharmony_ci .channels_min = 2, 7038c2ecf20Sopenharmony_ci .channels_max = 2, 7048c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16, 7058c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 7068c2ecf20Sopenharmony_ci }, 7078c2ecf20Sopenharmony_ci .capture = { 7088c2ecf20Sopenharmony_ci .channels_min = 2, 7098c2ecf20Sopenharmony_ci .channels_max = 2, 7108c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16, 7118c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 7128c2ecf20Sopenharmony_ci }, 7138c2ecf20Sopenharmony_ci .ops = &siu_dai_ops, 7148c2ecf20Sopenharmony_ci}; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic int siu_probe(struct platform_device *pdev) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci const struct firmware *fw_entry; 7198c2ecf20Sopenharmony_ci struct resource *res, *region; 7208c2ecf20Sopenharmony_ci struct siu_info *info; 7218c2ecf20Sopenharmony_ci int ret; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci info = devm_kmalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 7248c2ecf20Sopenharmony_ci if (!info) 7258c2ecf20Sopenharmony_ci return -ENOMEM; 7268c2ecf20Sopenharmony_ci siu_i2s_data = info; 7278c2ecf20Sopenharmony_ci info->dev = &pdev->dev; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev); 7308c2ecf20Sopenharmony_ci if (ret) 7318c2ecf20Sopenharmony_ci return ret; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci /* 7348c2ecf20Sopenharmony_ci * Loaded firmware is "const" - read only, but we have to modify it in 7358c2ecf20Sopenharmony_ci * snd_siu_sh7343_spbAselect() and snd_siu_sh7343_spbBselect() 7368c2ecf20Sopenharmony_ci */ 7378c2ecf20Sopenharmony_ci memcpy(&info->fw, fw_entry->data, fw_entry->size); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci release_firmware(fw_entry); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 7428c2ecf20Sopenharmony_ci if (!res) 7438c2ecf20Sopenharmony_ci return -ENODEV; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci region = devm_request_mem_region(&pdev->dev, res->start, 7468c2ecf20Sopenharmony_ci resource_size(res), pdev->name); 7478c2ecf20Sopenharmony_ci if (!region) { 7488c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "SIU region already claimed\n"); 7498c2ecf20Sopenharmony_ci return -EBUSY; 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci info->pram = devm_ioremap(&pdev->dev, res->start, PRAM_SIZE); 7538c2ecf20Sopenharmony_ci if (!info->pram) 7548c2ecf20Sopenharmony_ci return -ENOMEM; 7558c2ecf20Sopenharmony_ci info->xram = devm_ioremap(&pdev->dev, res->start + XRAM_OFFSET, 7568c2ecf20Sopenharmony_ci XRAM_SIZE); 7578c2ecf20Sopenharmony_ci if (!info->xram) 7588c2ecf20Sopenharmony_ci return -ENOMEM; 7598c2ecf20Sopenharmony_ci info->yram = devm_ioremap(&pdev->dev, res->start + YRAM_OFFSET, 7608c2ecf20Sopenharmony_ci YRAM_SIZE); 7618c2ecf20Sopenharmony_ci if (!info->yram) 7628c2ecf20Sopenharmony_ci return -ENOMEM; 7638c2ecf20Sopenharmony_ci info->reg = devm_ioremap(&pdev->dev, res->start + REG_OFFSET, 7648c2ecf20Sopenharmony_ci resource_size(res) - REG_OFFSET); 7658c2ecf20Sopenharmony_ci if (!info->reg) 7668c2ecf20Sopenharmony_ci return -ENOMEM; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, info); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci /* register using ARRAY version so we can keep dai name */ 7718c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, &siu_component, 7728c2ecf20Sopenharmony_ci &siu_i2s_dai, 1); 7738c2ecf20Sopenharmony_ci if (ret < 0) 7748c2ecf20Sopenharmony_ci return ret; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci return 0; 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic int siu_remove(struct platform_device *pdev) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 7848c2ecf20Sopenharmony_ci return 0; 7858c2ecf20Sopenharmony_ci} 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_cistatic struct platform_driver siu_driver = { 7888c2ecf20Sopenharmony_ci .driver = { 7898c2ecf20Sopenharmony_ci .name = "siu-pcm-audio", 7908c2ecf20Sopenharmony_ci }, 7918c2ecf20Sopenharmony_ci .probe = siu_probe, 7928c2ecf20Sopenharmony_ci .remove = siu_remove, 7938c2ecf20Sopenharmony_ci}; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_cimodule_platform_driver(siu_driver); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ciMODULE_AUTHOR("Carlos Munoz <carlos@kenati.com>"); 7988c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC SH7722 SIU driver"); 7998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 800