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