162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// siu_dai.c - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral. 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> 662306a36Sopenharmony_ci// Copyright (C) 2006 Carlos Munoz <carlos@kenati.com> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/firmware.h> 1062306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <asm/clock.h> 1562306a36Sopenharmony_ci#include <asm/siu.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <sound/control.h> 1862306a36Sopenharmony_ci#include <sound/soc.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "siu.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* Board specifics */ 2362306a36Sopenharmony_ci#if defined(CONFIG_CPU_SUBTYPE_SH7722) 2462306a36Sopenharmony_ci# define SIU_MAX_VOLUME 0x1000 2562306a36Sopenharmony_ci#else 2662306a36Sopenharmony_ci# define SIU_MAX_VOLUME 0x7fff 2762306a36Sopenharmony_ci#endif 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define PRAM_SIZE 0x2000 3062306a36Sopenharmony_ci#define XRAM_SIZE 0x800 3162306a36Sopenharmony_ci#define YRAM_SIZE 0x800 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define XRAM_OFFSET 0x4000 3462306a36Sopenharmony_ci#define YRAM_OFFSET 0x6000 3562306a36Sopenharmony_ci#define REG_OFFSET 0xc000 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define PLAYBACK_ENABLED 1 3862306a36Sopenharmony_ci#define CAPTURE_ENABLED 2 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define VOLUME_CAPTURE 0 4162306a36Sopenharmony_ci#define VOLUME_PLAYBACK 1 4262306a36Sopenharmony_ci#define DFLT_VOLUME_LEVEL 0x08000800 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * SPDIF is only available on port A and on some SIU implementations it is only 4662306a36Sopenharmony_ci * available for input. Due to the lack of hardware to test it, SPDIF is left 4762306a36Sopenharmony_ci * disabled in this driver version 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_cistruct format_flag { 5062306a36Sopenharmony_ci u32 i2s; 5162306a36Sopenharmony_ci u32 pcm; 5262306a36Sopenharmony_ci u32 spdif; 5362306a36Sopenharmony_ci u32 mask; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistruct port_flag { 5762306a36Sopenharmony_ci struct format_flag playback; 5862306a36Sopenharmony_ci struct format_flag capture; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct siu_info *siu_i2s_data; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic struct port_flag siu_flags[SIU_PORT_NUM] = { 6462306a36Sopenharmony_ci [SIU_PORT_A] = { 6562306a36Sopenharmony_ci .playback = { 6662306a36Sopenharmony_ci .i2s = 0x50000000, 6762306a36Sopenharmony_ci .pcm = 0x40000000, 6862306a36Sopenharmony_ci .spdif = 0x80000000, /* not on all SIU versions */ 6962306a36Sopenharmony_ci .mask = 0xd0000000, 7062306a36Sopenharmony_ci }, 7162306a36Sopenharmony_ci .capture = { 7262306a36Sopenharmony_ci .i2s = 0x05000000, 7362306a36Sopenharmony_ci .pcm = 0x04000000, 7462306a36Sopenharmony_ci .spdif = 0x08000000, 7562306a36Sopenharmony_ci .mask = 0x0d000000, 7662306a36Sopenharmony_ci }, 7762306a36Sopenharmony_ci }, 7862306a36Sopenharmony_ci [SIU_PORT_B] = { 7962306a36Sopenharmony_ci .playback = { 8062306a36Sopenharmony_ci .i2s = 0x00500000, 8162306a36Sopenharmony_ci .pcm = 0x00400000, 8262306a36Sopenharmony_ci .spdif = 0, /* impossible - turn off */ 8362306a36Sopenharmony_ci .mask = 0x00500000, 8462306a36Sopenharmony_ci }, 8562306a36Sopenharmony_ci .capture = { 8662306a36Sopenharmony_ci .i2s = 0x00050000, 8762306a36Sopenharmony_ci .pcm = 0x00040000, 8862306a36Sopenharmony_ci .spdif = 0, /* impossible - turn off */ 8962306a36Sopenharmony_ci .mask = 0x00050000, 9062306a36Sopenharmony_ci }, 9162306a36Sopenharmony_ci }, 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void siu_dai_start(struct siu_port *port_info) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct siu_info *info = siu_i2s_data; 9762306a36Sopenharmony_ci u32 __iomem *base = info->reg; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci dev_dbg(port_info->pcm->card->dev, "%s\n", __func__); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Issue software reset to siu */ 10262306a36Sopenharmony_ci siu_write32(base + SIU_SRCTL, 0); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* Wait for the reset to take effect */ 10562306a36Sopenharmony_ci udelay(1); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci port_info->stfifo = 0; 10862306a36Sopenharmony_ci port_info->trdat = 0; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* portA, portB, SIU operate */ 11162306a36Sopenharmony_ci siu_write32(base + SIU_SRCTL, 0x301); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* portA=256fs, portB=256fs */ 11462306a36Sopenharmony_ci siu_write32(base + SIU_CKCTL, 0x40400000); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* portA's BRG does not divide SIUCKA */ 11762306a36Sopenharmony_ci siu_write32(base + SIU_BRGASEL, 0); 11862306a36Sopenharmony_ci siu_write32(base + SIU_BRRA, 0); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* portB's BRG divides SIUCKB by half */ 12162306a36Sopenharmony_ci siu_write32(base + SIU_BRGBSEL, 1); 12262306a36Sopenharmony_ci siu_write32(base + SIU_BRRB, 0); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci siu_write32(base + SIU_IFCTL, 0x44440000); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* portA: 32 bit/fs, master; portB: 32 bit/fs, master */ 12762306a36Sopenharmony_ci siu_write32(base + SIU_SFORM, 0x0c0c0000); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* 13062306a36Sopenharmony_ci * Volume levels: looks like the DSP firmware implements volume controls 13162306a36Sopenharmony_ci * differently from what's described in the datasheet 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci siu_write32(base + SIU_SBDVCA, port_info->playback.volume); 13462306a36Sopenharmony_ci siu_write32(base + SIU_SBDVCB, port_info->capture.volume); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic void siu_dai_stop(struct siu_port *port_info) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct siu_info *info = siu_i2s_data; 14062306a36Sopenharmony_ci u32 __iomem *base = info->reg; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* SIU software reset */ 14362306a36Sopenharmony_ci siu_write32(base + SIU_SRCTL, 0); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic void siu_dai_spbAselect(struct siu_port *port_info) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct siu_info *info = siu_i2s_data; 14962306a36Sopenharmony_ci struct siu_firmware *fw = &info->fw; 15062306a36Sopenharmony_ci u32 *ydef = fw->yram0; 15162306a36Sopenharmony_ci u32 idx; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* path A use */ 15462306a36Sopenharmony_ci if (!info->port_id) 15562306a36Sopenharmony_ci idx = 1; /* portA */ 15662306a36Sopenharmony_ci else 15762306a36Sopenharmony_ci idx = 2; /* portB */ 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci ydef[0] = (fw->spbpar[idx].ab1a << 16) | 16062306a36Sopenharmony_ci (fw->spbpar[idx].ab0a << 8) | 16162306a36Sopenharmony_ci (fw->spbpar[idx].dir << 7) | 3; 16262306a36Sopenharmony_ci ydef[1] = fw->yram0[1]; /* 0x03000300 */ 16362306a36Sopenharmony_ci ydef[2] = (16 / 2) << 24; 16462306a36Sopenharmony_ci ydef[3] = fw->yram0[3]; /* 0 */ 16562306a36Sopenharmony_ci ydef[4] = fw->yram0[4]; /* 0 */ 16662306a36Sopenharmony_ci ydef[7] = fw->spbpar[idx].event; 16762306a36Sopenharmony_ci port_info->stfifo |= fw->spbpar[idx].stfifo; 16862306a36Sopenharmony_ci port_info->trdat |= fw->spbpar[idx].trdat; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void siu_dai_spbBselect(struct siu_port *port_info) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct siu_info *info = siu_i2s_data; 17462306a36Sopenharmony_ci struct siu_firmware *fw = &info->fw; 17562306a36Sopenharmony_ci u32 *ydef = fw->yram0; 17662306a36Sopenharmony_ci u32 idx; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* path B use */ 17962306a36Sopenharmony_ci if (!info->port_id) 18062306a36Sopenharmony_ci idx = 7; /* portA */ 18162306a36Sopenharmony_ci else 18262306a36Sopenharmony_ci idx = 8; /* portB */ 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci ydef[5] = (fw->spbpar[idx].ab1a << 16) | 18562306a36Sopenharmony_ci (fw->spbpar[idx].ab0a << 8) | 1; 18662306a36Sopenharmony_ci ydef[6] = fw->spbpar[idx].event; 18762306a36Sopenharmony_ci port_info->stfifo |= fw->spbpar[idx].stfifo; 18862306a36Sopenharmony_ci port_info->trdat |= fw->spbpar[idx].trdat; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic void siu_dai_open(struct siu_stream *siu_stream) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct siu_info *info = siu_i2s_data; 19462306a36Sopenharmony_ci u32 __iomem *base = info->reg; 19562306a36Sopenharmony_ci u32 srctl, ifctl; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci srctl = siu_read32(base + SIU_SRCTL); 19862306a36Sopenharmony_ci ifctl = siu_read32(base + SIU_IFCTL); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci switch (info->port_id) { 20162306a36Sopenharmony_ci case SIU_PORT_A: 20262306a36Sopenharmony_ci /* portA operates */ 20362306a36Sopenharmony_ci srctl |= 0x200; 20462306a36Sopenharmony_ci ifctl &= ~0xc2; 20562306a36Sopenharmony_ci break; 20662306a36Sopenharmony_ci case SIU_PORT_B: 20762306a36Sopenharmony_ci /* portB operates */ 20862306a36Sopenharmony_ci srctl |= 0x100; 20962306a36Sopenharmony_ci ifctl &= ~0x31; 21062306a36Sopenharmony_ci break; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci siu_write32(base + SIU_SRCTL, srctl); 21462306a36Sopenharmony_ci /* Unmute and configure portA */ 21562306a36Sopenharmony_ci siu_write32(base + SIU_IFCTL, ifctl); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/* 21962306a36Sopenharmony_ci * At the moment only fixed Left-upper, Left-lower, Right-upper, Right-lower 22062306a36Sopenharmony_ci * packing is supported 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_cistatic void siu_dai_pcmdatapack(struct siu_stream *siu_stream) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct siu_info *info = siu_i2s_data; 22562306a36Sopenharmony_ci u32 __iomem *base = info->reg; 22662306a36Sopenharmony_ci u32 dpak; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci dpak = siu_read32(base + SIU_DPAK); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci switch (info->port_id) { 23162306a36Sopenharmony_ci case SIU_PORT_A: 23262306a36Sopenharmony_ci dpak &= ~0xc0000000; 23362306a36Sopenharmony_ci break; 23462306a36Sopenharmony_ci case SIU_PORT_B: 23562306a36Sopenharmony_ci dpak &= ~0x00c00000; 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci siu_write32(base + SIU_DPAK, dpak); 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic int siu_dai_spbstart(struct siu_port *port_info) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct siu_info *info = siu_i2s_data; 24562306a36Sopenharmony_ci u32 __iomem *base = info->reg; 24662306a36Sopenharmony_ci struct siu_firmware *fw = &info->fw; 24762306a36Sopenharmony_ci u32 *ydef = fw->yram0; 24862306a36Sopenharmony_ci int cnt; 24962306a36Sopenharmony_ci u32 __iomem *add; 25062306a36Sopenharmony_ci u32 *ptr; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Load SPB Program in PRAM */ 25362306a36Sopenharmony_ci ptr = fw->pram0; 25462306a36Sopenharmony_ci add = info->pram; 25562306a36Sopenharmony_ci for (cnt = 0; cnt < PRAM0_SIZE; cnt++, add++, ptr++) 25662306a36Sopenharmony_ci siu_write32(add, *ptr); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci ptr = fw->pram1; 25962306a36Sopenharmony_ci add = info->pram + (0x0100 / sizeof(u32)); 26062306a36Sopenharmony_ci for (cnt = 0; cnt < PRAM1_SIZE; cnt++, add++, ptr++) 26162306a36Sopenharmony_ci siu_write32(add, *ptr); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* XRAM initialization */ 26462306a36Sopenharmony_ci add = info->xram; 26562306a36Sopenharmony_ci for (cnt = 0; cnt < XRAM0_SIZE + XRAM1_SIZE + XRAM2_SIZE; cnt++, add++) 26662306a36Sopenharmony_ci siu_write32(add, 0); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* YRAM variable area initialization */ 26962306a36Sopenharmony_ci add = info->yram; 27062306a36Sopenharmony_ci for (cnt = 0; cnt < YRAM_DEF_SIZE; cnt++, add++) 27162306a36Sopenharmony_ci siu_write32(add, ydef[cnt]); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* YRAM FIR coefficient area initialization */ 27462306a36Sopenharmony_ci add = info->yram + (0x0200 / sizeof(u32)); 27562306a36Sopenharmony_ci for (cnt = 0; cnt < YRAM_FIR_SIZE; cnt++, add++) 27662306a36Sopenharmony_ci siu_write32(add, fw->yram_fir_coeff[cnt]); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* YRAM IIR coefficient area initialization */ 27962306a36Sopenharmony_ci add = info->yram + (0x0600 / sizeof(u32)); 28062306a36Sopenharmony_ci for (cnt = 0; cnt < YRAM_IIR_SIZE; cnt++, add++) 28162306a36Sopenharmony_ci siu_write32(add, 0); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci siu_write32(base + SIU_TRDAT, port_info->trdat); 28462306a36Sopenharmony_ci port_info->trdat = 0x0; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* SPB start condition: software */ 28862306a36Sopenharmony_ci siu_write32(base + SIU_SBACTIV, 0); 28962306a36Sopenharmony_ci /* Start SPB */ 29062306a36Sopenharmony_ci siu_write32(base + SIU_SBCTL, 0xc0000000); 29162306a36Sopenharmony_ci /* Wait for program to halt */ 29262306a36Sopenharmony_ci cnt = 0x10000; 29362306a36Sopenharmony_ci while (--cnt && siu_read32(base + SIU_SBCTL) != 0x80000000) 29462306a36Sopenharmony_ci cpu_relax(); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (!cnt) 29762306a36Sopenharmony_ci return -EBUSY; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* SPB program start address setting */ 30062306a36Sopenharmony_ci siu_write32(base + SIU_SBPSET, 0x00400000); 30162306a36Sopenharmony_ci /* SPB hardware start(FIFOCTL source) */ 30262306a36Sopenharmony_ci siu_write32(base + SIU_SBACTIV, 0xc0000000); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic void siu_dai_spbstop(struct siu_port *port_info) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct siu_info *info = siu_i2s_data; 31062306a36Sopenharmony_ci u32 __iomem *base = info->reg; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci siu_write32(base + SIU_SBACTIV, 0); 31362306a36Sopenharmony_ci /* SPB stop */ 31462306a36Sopenharmony_ci siu_write32(base + SIU_SBCTL, 0); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci port_info->stfifo = 0; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/* API functions */ 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci/* Playback and capture hardware properties are identical */ 32262306a36Sopenharmony_cistatic const struct snd_pcm_hardware siu_dai_pcm_hw = { 32362306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_INTERLEAVED, 32462306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16, 32562306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 32662306a36Sopenharmony_ci .rate_min = 8000, 32762306a36Sopenharmony_ci .rate_max = 48000, 32862306a36Sopenharmony_ci .channels_min = 2, 32962306a36Sopenharmony_ci .channels_max = 2, 33062306a36Sopenharmony_ci .buffer_bytes_max = SIU_BUFFER_BYTES_MAX, 33162306a36Sopenharmony_ci .period_bytes_min = SIU_PERIOD_BYTES_MIN, 33262306a36Sopenharmony_ci .period_bytes_max = SIU_PERIOD_BYTES_MAX, 33362306a36Sopenharmony_ci .periods_min = SIU_PERIODS_MIN, 33462306a36Sopenharmony_ci .periods_max = SIU_PERIODS_MAX, 33562306a36Sopenharmony_ci}; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic int siu_dai_info_volume(struct snd_kcontrol *kctrl, 33862306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct siu_port *port_info = snd_kcontrol_chip(kctrl); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci dev_dbg(port_info->pcm->card->dev, "%s\n", __func__); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 34562306a36Sopenharmony_ci uinfo->count = 2; 34662306a36Sopenharmony_ci uinfo->value.integer.min = 0; 34762306a36Sopenharmony_ci uinfo->value.integer.max = SIU_MAX_VOLUME; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int siu_dai_get_volume(struct snd_kcontrol *kctrl, 35362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct siu_port *port_info = snd_kcontrol_chip(kctrl); 35662306a36Sopenharmony_ci struct device *dev = port_info->pcm->card->dev; 35762306a36Sopenharmony_ci u32 vol; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci dev_dbg(dev, "%s\n", __func__); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci switch (kctrl->private_value) { 36262306a36Sopenharmony_ci case VOLUME_PLAYBACK: 36362306a36Sopenharmony_ci /* Playback is always on port 0 */ 36462306a36Sopenharmony_ci vol = port_info->playback.volume; 36562306a36Sopenharmony_ci ucontrol->value.integer.value[0] = vol & 0xffff; 36662306a36Sopenharmony_ci ucontrol->value.integer.value[1] = vol >> 16 & 0xffff; 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci case VOLUME_CAPTURE: 36962306a36Sopenharmony_ci /* Capture is always on port 1 */ 37062306a36Sopenharmony_ci vol = port_info->capture.volume; 37162306a36Sopenharmony_ci ucontrol->value.integer.value[0] = vol & 0xffff; 37262306a36Sopenharmony_ci ucontrol->value.integer.value[1] = vol >> 16 & 0xffff; 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci default: 37562306a36Sopenharmony_ci dev_err(dev, "%s() invalid private_value=%ld\n", 37662306a36Sopenharmony_ci __func__, kctrl->private_value); 37762306a36Sopenharmony_ci return -EINVAL; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic int siu_dai_put_volume(struct snd_kcontrol *kctrl, 38462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct siu_port *port_info = snd_kcontrol_chip(kctrl); 38762306a36Sopenharmony_ci struct device *dev = port_info->pcm->card->dev; 38862306a36Sopenharmony_ci struct siu_info *info = siu_i2s_data; 38962306a36Sopenharmony_ci u32 __iomem *base = info->reg; 39062306a36Sopenharmony_ci u32 new_vol; 39162306a36Sopenharmony_ci u32 cur_vol; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci dev_dbg(dev, "%s\n", __func__); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (ucontrol->value.integer.value[0] < 0 || 39662306a36Sopenharmony_ci ucontrol->value.integer.value[0] > SIU_MAX_VOLUME || 39762306a36Sopenharmony_ci ucontrol->value.integer.value[1] < 0 || 39862306a36Sopenharmony_ci ucontrol->value.integer.value[1] > SIU_MAX_VOLUME) 39962306a36Sopenharmony_ci return -EINVAL; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci new_vol = ucontrol->value.integer.value[0] | 40262306a36Sopenharmony_ci ucontrol->value.integer.value[1] << 16; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* See comment above - DSP firmware implementation */ 40562306a36Sopenharmony_ci switch (kctrl->private_value) { 40662306a36Sopenharmony_ci case VOLUME_PLAYBACK: 40762306a36Sopenharmony_ci /* Playback is always on port 0 */ 40862306a36Sopenharmony_ci cur_vol = port_info->playback.volume; 40962306a36Sopenharmony_ci siu_write32(base + SIU_SBDVCA, new_vol); 41062306a36Sopenharmony_ci port_info->playback.volume = new_vol; 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci case VOLUME_CAPTURE: 41362306a36Sopenharmony_ci /* Capture is always on port 1 */ 41462306a36Sopenharmony_ci cur_vol = port_info->capture.volume; 41562306a36Sopenharmony_ci siu_write32(base + SIU_SBDVCB, new_vol); 41662306a36Sopenharmony_ci port_info->capture.volume = new_vol; 41762306a36Sopenharmony_ci break; 41862306a36Sopenharmony_ci default: 41962306a36Sopenharmony_ci dev_err(dev, "%s() invalid private_value=%ld\n", 42062306a36Sopenharmony_ci __func__, kctrl->private_value); 42162306a36Sopenharmony_ci return -EINVAL; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (cur_vol != new_vol) 42562306a36Sopenharmony_ci return 1; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return 0; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic const struct snd_kcontrol_new playback_controls = { 43162306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 43262306a36Sopenharmony_ci .name = "PCM Playback Volume", 43362306a36Sopenharmony_ci .index = 0, 43462306a36Sopenharmony_ci .info = siu_dai_info_volume, 43562306a36Sopenharmony_ci .get = siu_dai_get_volume, 43662306a36Sopenharmony_ci .put = siu_dai_put_volume, 43762306a36Sopenharmony_ci .private_value = VOLUME_PLAYBACK, 43862306a36Sopenharmony_ci}; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic const struct snd_kcontrol_new capture_controls = { 44162306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 44262306a36Sopenharmony_ci .name = "PCM Capture Volume", 44362306a36Sopenharmony_ci .index = 0, 44462306a36Sopenharmony_ci .info = siu_dai_info_volume, 44562306a36Sopenharmony_ci .get = siu_dai_get_volume, 44662306a36Sopenharmony_ci .put = siu_dai_put_volume, 44762306a36Sopenharmony_ci .private_value = VOLUME_CAPTURE, 44862306a36Sopenharmony_ci}; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ciint siu_init_port(int port, struct siu_port **port_info, struct snd_card *card) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct device *dev = card->dev; 45362306a36Sopenharmony_ci struct snd_kcontrol *kctrl; 45462306a36Sopenharmony_ci int ret; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci *port_info = kzalloc(sizeof(**port_info), GFP_KERNEL); 45762306a36Sopenharmony_ci if (!*port_info) 45862306a36Sopenharmony_ci return -ENOMEM; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci dev_dbg(dev, "%s: port #%d@%p\n", __func__, port, *port_info); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci (*port_info)->playback.volume = DFLT_VOLUME_LEVEL; 46362306a36Sopenharmony_ci (*port_info)->capture.volume = DFLT_VOLUME_LEVEL; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* 46662306a36Sopenharmony_ci * Add mixer support. The SPB is used to change the volume. Both 46762306a36Sopenharmony_ci * ports use the same SPB. Therefore, we only register one 46862306a36Sopenharmony_ci * control instance since it will be used by both channels. 46962306a36Sopenharmony_ci * In error case we continue without controls. 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_ci kctrl = snd_ctl_new1(&playback_controls, *port_info); 47262306a36Sopenharmony_ci ret = snd_ctl_add(card, kctrl); 47362306a36Sopenharmony_ci if (ret < 0) 47462306a36Sopenharmony_ci dev_err(dev, 47562306a36Sopenharmony_ci "failed to add playback controls %p port=%d err=%d\n", 47662306a36Sopenharmony_ci kctrl, port, ret); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci kctrl = snd_ctl_new1(&capture_controls, *port_info); 47962306a36Sopenharmony_ci ret = snd_ctl_add(card, kctrl); 48062306a36Sopenharmony_ci if (ret < 0) 48162306a36Sopenharmony_ci dev_err(dev, 48262306a36Sopenharmony_ci "failed to add capture controls %p port=%d err=%d\n", 48362306a36Sopenharmony_ci kctrl, port, ret); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci return 0; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_civoid siu_free_port(struct siu_port *port_info) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci kfree(port_info); 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic int siu_dai_startup(struct snd_pcm_substream *substream, 49462306a36Sopenharmony_ci struct snd_soc_dai *dai) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct siu_info *info = snd_soc_dai_get_drvdata(dai); 49762306a36Sopenharmony_ci struct snd_pcm_runtime *rt = substream->runtime; 49862306a36Sopenharmony_ci struct siu_port *port_info = siu_port_info(substream); 49962306a36Sopenharmony_ci int ret; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__, 50262306a36Sopenharmony_ci info->port_id, port_info); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci snd_soc_set_runtime_hwparams(substream, &siu_dai_pcm_hw); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); 50762306a36Sopenharmony_ci if (unlikely(ret < 0)) 50862306a36Sopenharmony_ci return ret; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci siu_dai_start(port_info); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci return 0; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic void siu_dai_shutdown(struct snd_pcm_substream *substream, 51662306a36Sopenharmony_ci struct snd_soc_dai *dai) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct siu_info *info = snd_soc_dai_get_drvdata(dai); 51962306a36Sopenharmony_ci struct siu_port *port_info = siu_port_info(substream); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__, 52262306a36Sopenharmony_ci info->port_id, port_info); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 52562306a36Sopenharmony_ci port_info->play_cap &= ~PLAYBACK_ENABLED; 52662306a36Sopenharmony_ci else 52762306a36Sopenharmony_ci port_info->play_cap &= ~CAPTURE_ENABLED; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* Stop the siu if the other stream is not using it */ 53062306a36Sopenharmony_ci if (!port_info->play_cap) { 53162306a36Sopenharmony_ci /* during stmread or stmwrite ? */ 53262306a36Sopenharmony_ci if (WARN_ON(port_info->playback.rw_flg || port_info->capture.rw_flg)) 53362306a36Sopenharmony_ci return; 53462306a36Sopenharmony_ci siu_dai_spbstop(port_info); 53562306a36Sopenharmony_ci siu_dai_stop(port_info); 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci/* PCM part of siu_dai_playback_prepare() / siu_dai_capture_prepare() */ 54062306a36Sopenharmony_cistatic int siu_dai_prepare(struct snd_pcm_substream *substream, 54162306a36Sopenharmony_ci struct snd_soc_dai *dai) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci struct siu_info *info = snd_soc_dai_get_drvdata(dai); 54462306a36Sopenharmony_ci struct snd_pcm_runtime *rt = substream->runtime; 54562306a36Sopenharmony_ci struct siu_port *port_info = siu_port_info(substream); 54662306a36Sopenharmony_ci struct siu_stream *siu_stream; 54762306a36Sopenharmony_ci int self, ret; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci dev_dbg(substream->pcm->card->dev, 55062306a36Sopenharmony_ci "%s: port %d, active streams %lx, %d channels\n", 55162306a36Sopenharmony_ci __func__, info->port_id, port_info->play_cap, rt->channels); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 55462306a36Sopenharmony_ci self = PLAYBACK_ENABLED; 55562306a36Sopenharmony_ci siu_stream = &port_info->playback; 55662306a36Sopenharmony_ci } else { 55762306a36Sopenharmony_ci self = CAPTURE_ENABLED; 55862306a36Sopenharmony_ci siu_stream = &port_info->capture; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* Set up the siu if not already done */ 56262306a36Sopenharmony_ci if (!port_info->play_cap) { 56362306a36Sopenharmony_ci siu_stream->rw_flg = 0; /* stream-data transfer flag */ 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci siu_dai_spbAselect(port_info); 56662306a36Sopenharmony_ci siu_dai_spbBselect(port_info); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci siu_dai_open(siu_stream); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci siu_dai_pcmdatapack(siu_stream); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci ret = siu_dai_spbstart(port_info); 57362306a36Sopenharmony_ci if (ret < 0) 57462306a36Sopenharmony_ci goto fail; 57562306a36Sopenharmony_ci } else { 57662306a36Sopenharmony_ci ret = 0; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci port_info->play_cap |= self; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cifail: 58262306a36Sopenharmony_ci return ret; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci/* 58662306a36Sopenharmony_ci * SIU can set bus format to I2S / PCM / SPDIF independently for playback and 58762306a36Sopenharmony_ci * capture, however, the current API sets the bus format globally for a DAI. 58862306a36Sopenharmony_ci */ 58962306a36Sopenharmony_cistatic int siu_dai_set_fmt(struct snd_soc_dai *dai, 59062306a36Sopenharmony_ci unsigned int fmt) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct siu_info *info = snd_soc_dai_get_drvdata(dai); 59362306a36Sopenharmony_ci u32 __iomem *base = info->reg; 59462306a36Sopenharmony_ci u32 ifctl; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci dev_dbg(dai->dev, "%s: fmt 0x%x on port %d\n", 59762306a36Sopenharmony_ci __func__, fmt, info->port_id); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (info->port_id < 0) 60062306a36Sopenharmony_ci return -ENODEV; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci /* Here select between I2S / PCM / SPDIF */ 60362306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 60462306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 60562306a36Sopenharmony_ci ifctl = siu_flags[info->port_id].playback.i2s | 60662306a36Sopenharmony_ci siu_flags[info->port_id].capture.i2s; 60762306a36Sopenharmony_ci break; 60862306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 60962306a36Sopenharmony_ci ifctl = siu_flags[info->port_id].playback.pcm | 61062306a36Sopenharmony_ci siu_flags[info->port_id].capture.pcm; 61162306a36Sopenharmony_ci break; 61262306a36Sopenharmony_ci /* SPDIF disabled - see comment at the top */ 61362306a36Sopenharmony_ci default: 61462306a36Sopenharmony_ci return -EINVAL; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci ifctl |= ~(siu_flags[info->port_id].playback.mask | 61862306a36Sopenharmony_ci siu_flags[info->port_id].capture.mask) & 61962306a36Sopenharmony_ci siu_read32(base + SIU_IFCTL); 62062306a36Sopenharmony_ci siu_write32(base + SIU_IFCTL, ifctl); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci return 0; 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic int siu_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, 62662306a36Sopenharmony_ci unsigned int freq, int dir) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci struct clk *siu_clk, *parent_clk; 62962306a36Sopenharmony_ci char *siu_name, *parent_name; 63062306a36Sopenharmony_ci int ret; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci if (dir != SND_SOC_CLOCK_IN) 63362306a36Sopenharmony_ci return -EINVAL; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci dev_dbg(dai->dev, "%s: using clock %d\n", __func__, clk_id); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci switch (clk_id) { 63862306a36Sopenharmony_ci case SIU_CLKA_PLL: 63962306a36Sopenharmony_ci siu_name = "siua_clk"; 64062306a36Sopenharmony_ci parent_name = "pll_clk"; 64162306a36Sopenharmony_ci break; 64262306a36Sopenharmony_ci case SIU_CLKA_EXT: 64362306a36Sopenharmony_ci siu_name = "siua_clk"; 64462306a36Sopenharmony_ci parent_name = "siumcka_clk"; 64562306a36Sopenharmony_ci break; 64662306a36Sopenharmony_ci case SIU_CLKB_PLL: 64762306a36Sopenharmony_ci siu_name = "siub_clk"; 64862306a36Sopenharmony_ci parent_name = "pll_clk"; 64962306a36Sopenharmony_ci break; 65062306a36Sopenharmony_ci case SIU_CLKB_EXT: 65162306a36Sopenharmony_ci siu_name = "siub_clk"; 65262306a36Sopenharmony_ci parent_name = "siumckb_clk"; 65362306a36Sopenharmony_ci break; 65462306a36Sopenharmony_ci default: 65562306a36Sopenharmony_ci return -EINVAL; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci siu_clk = clk_get(dai->dev, siu_name); 65962306a36Sopenharmony_ci if (IS_ERR(siu_clk)) { 66062306a36Sopenharmony_ci dev_err(dai->dev, "%s: cannot get a SIU clock: %ld\n", __func__, 66162306a36Sopenharmony_ci PTR_ERR(siu_clk)); 66262306a36Sopenharmony_ci return PTR_ERR(siu_clk); 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci parent_clk = clk_get(dai->dev, parent_name); 66662306a36Sopenharmony_ci if (IS_ERR(parent_clk)) { 66762306a36Sopenharmony_ci ret = PTR_ERR(parent_clk); 66862306a36Sopenharmony_ci dev_err(dai->dev, "cannot get a SIU clock parent: %d\n", ret); 66962306a36Sopenharmony_ci goto epclkget; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci ret = clk_set_parent(siu_clk, parent_clk); 67362306a36Sopenharmony_ci if (ret < 0) { 67462306a36Sopenharmony_ci dev_err(dai->dev, "cannot reparent the SIU clock: %d\n", ret); 67562306a36Sopenharmony_ci goto eclksetp; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci ret = clk_set_rate(siu_clk, freq); 67962306a36Sopenharmony_ci if (ret < 0) 68062306a36Sopenharmony_ci dev_err(dai->dev, "cannot set SIU clock rate: %d\n", ret); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* TODO: when clkdev gets reference counting we'll move these to siu_dai_shutdown() */ 68362306a36Sopenharmony_cieclksetp: 68462306a36Sopenharmony_ci clk_put(parent_clk); 68562306a36Sopenharmony_ciepclkget: 68662306a36Sopenharmony_ci clk_put(siu_clk); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci return ret; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic const struct snd_soc_dai_ops siu_dai_ops = { 69262306a36Sopenharmony_ci .startup = siu_dai_startup, 69362306a36Sopenharmony_ci .shutdown = siu_dai_shutdown, 69462306a36Sopenharmony_ci .prepare = siu_dai_prepare, 69562306a36Sopenharmony_ci .set_sysclk = siu_dai_set_sysclk, 69662306a36Sopenharmony_ci .set_fmt = siu_dai_set_fmt, 69762306a36Sopenharmony_ci}; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic struct snd_soc_dai_driver siu_i2s_dai = { 70062306a36Sopenharmony_ci .name = "siu-i2s-dai", 70162306a36Sopenharmony_ci .playback = { 70262306a36Sopenharmony_ci .channels_min = 2, 70362306a36Sopenharmony_ci .channels_max = 2, 70462306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16, 70562306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 70662306a36Sopenharmony_ci }, 70762306a36Sopenharmony_ci .capture = { 70862306a36Sopenharmony_ci .channels_min = 2, 70962306a36Sopenharmony_ci .channels_max = 2, 71062306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16, 71162306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 71262306a36Sopenharmony_ci }, 71362306a36Sopenharmony_ci .ops = &siu_dai_ops, 71462306a36Sopenharmony_ci}; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic int siu_probe(struct platform_device *pdev) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci const struct firmware *fw_entry; 71962306a36Sopenharmony_ci struct resource *res, *region; 72062306a36Sopenharmony_ci struct siu_info *info; 72162306a36Sopenharmony_ci int ret; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci info = devm_kmalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 72462306a36Sopenharmony_ci if (!info) 72562306a36Sopenharmony_ci return -ENOMEM; 72662306a36Sopenharmony_ci siu_i2s_data = info; 72762306a36Sopenharmony_ci info->dev = &pdev->dev; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev); 73062306a36Sopenharmony_ci if (ret) 73162306a36Sopenharmony_ci return ret; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* 73462306a36Sopenharmony_ci * Loaded firmware is "const" - read only, but we have to modify it in 73562306a36Sopenharmony_ci * snd_siu_sh7343_spbAselect() and snd_siu_sh7343_spbBselect() 73662306a36Sopenharmony_ci */ 73762306a36Sopenharmony_ci memcpy(&info->fw, fw_entry->data, fw_entry->size); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci release_firmware(fw_entry); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 74262306a36Sopenharmony_ci if (!res) 74362306a36Sopenharmony_ci return -ENODEV; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci region = devm_request_mem_region(&pdev->dev, res->start, 74662306a36Sopenharmony_ci resource_size(res), pdev->name); 74762306a36Sopenharmony_ci if (!region) { 74862306a36Sopenharmony_ci dev_err(&pdev->dev, "SIU region already claimed\n"); 74962306a36Sopenharmony_ci return -EBUSY; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci info->pram = devm_ioremap(&pdev->dev, res->start, PRAM_SIZE); 75362306a36Sopenharmony_ci if (!info->pram) 75462306a36Sopenharmony_ci return -ENOMEM; 75562306a36Sopenharmony_ci info->xram = devm_ioremap(&pdev->dev, res->start + XRAM_OFFSET, 75662306a36Sopenharmony_ci XRAM_SIZE); 75762306a36Sopenharmony_ci if (!info->xram) 75862306a36Sopenharmony_ci return -ENOMEM; 75962306a36Sopenharmony_ci info->yram = devm_ioremap(&pdev->dev, res->start + YRAM_OFFSET, 76062306a36Sopenharmony_ci YRAM_SIZE); 76162306a36Sopenharmony_ci if (!info->yram) 76262306a36Sopenharmony_ci return -ENOMEM; 76362306a36Sopenharmony_ci info->reg = devm_ioremap(&pdev->dev, res->start + REG_OFFSET, 76462306a36Sopenharmony_ci resource_size(res) - REG_OFFSET); 76562306a36Sopenharmony_ci if (!info->reg) 76662306a36Sopenharmony_ci return -ENOMEM; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci dev_set_drvdata(&pdev->dev, info); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* register using ARRAY version so we can keep dai name */ 77162306a36Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, &siu_component, 77262306a36Sopenharmony_ci &siu_i2s_dai, 1); 77362306a36Sopenharmony_ci if (ret < 0) 77462306a36Sopenharmony_ci return ret; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci return 0; 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic void siu_remove(struct platform_device *pdev) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic struct platform_driver siu_driver = { 78762306a36Sopenharmony_ci .driver = { 78862306a36Sopenharmony_ci .name = "siu-pcm-audio", 78962306a36Sopenharmony_ci }, 79062306a36Sopenharmony_ci .probe = siu_probe, 79162306a36Sopenharmony_ci .remove_new = siu_remove, 79262306a36Sopenharmony_ci}; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cimodule_platform_driver(siu_driver); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ciMODULE_AUTHOR("Carlos Munoz <carlos@kenati.com>"); 79762306a36Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC SH7722 SIU driver"); 79862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ciMODULE_FIRMWARE("siu_spb.bin"); 801