cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

jz4725b.c (16513B)


      1// SPDX-License-Identifier: GPL-2.0
      2//
      3// JZ4725B CODEC driver
      4//
      5// Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net>
      6
      7#include <linux/kernel.h>
      8#include <linux/module.h>
      9#include <linux/platform_device.h>
     10#include <linux/slab.h>
     11#include <linux/io.h>
     12#include <linux/iopoll.h>
     13#include <linux/regmap.h>
     14#include <linux/clk.h>
     15
     16#include <linux/delay.h>
     17
     18#include <sound/core.h>
     19#include <sound/pcm.h>
     20#include <sound/pcm_params.h>
     21#include <sound/initval.h>
     22#include <sound/soc.h>
     23#include <sound/tlv.h>
     24
     25#define ICDC_RGADW_OFFSET		0x00
     26#define ICDC_RGDATA_OFFSET		0x04
     27
     28/* ICDC internal register access control register(RGADW) */
     29#define ICDC_RGADW_RGWR			BIT(16)
     30
     31#define ICDC_RGADW_RGADDR_OFFSET	8
     32#define	ICDC_RGADW_RGADDR_MASK		GENMASK(14, ICDC_RGADW_RGADDR_OFFSET)
     33
     34#define ICDC_RGADW_RGDIN_OFFSET		0
     35#define	ICDC_RGADW_RGDIN_MASK		GENMASK(7, ICDC_RGADW_RGDIN_OFFSET)
     36
     37/* ICDC internal register data output register (RGDATA)*/
     38#define ICDC_RGDATA_IRQ			BIT(8)
     39
     40#define ICDC_RGDATA_RGDOUT_OFFSET	0
     41#define ICDC_RGDATA_RGDOUT_MASK		GENMASK(7, ICDC_RGDATA_RGDOUT_OFFSET)
     42
     43/* JZ internal register space */
     44enum {
     45	JZ4725B_CODEC_REG_AICR,
     46	JZ4725B_CODEC_REG_CR1,
     47	JZ4725B_CODEC_REG_CR2,
     48	JZ4725B_CODEC_REG_CCR1,
     49	JZ4725B_CODEC_REG_CCR2,
     50	JZ4725B_CODEC_REG_PMR1,
     51	JZ4725B_CODEC_REG_PMR2,
     52	JZ4725B_CODEC_REG_CRR,
     53	JZ4725B_CODEC_REG_ICR,
     54	JZ4725B_CODEC_REG_IFR,
     55	JZ4725B_CODEC_REG_CGR1,
     56	JZ4725B_CODEC_REG_CGR2,
     57	JZ4725B_CODEC_REG_CGR3,
     58	JZ4725B_CODEC_REG_CGR4,
     59	JZ4725B_CODEC_REG_CGR5,
     60	JZ4725B_CODEC_REG_CGR6,
     61	JZ4725B_CODEC_REG_CGR7,
     62	JZ4725B_CODEC_REG_CGR8,
     63	JZ4725B_CODEC_REG_CGR9,
     64	JZ4725B_CODEC_REG_CGR10,
     65	JZ4725B_CODEC_REG_TR1,
     66	JZ4725B_CODEC_REG_TR2,
     67	JZ4725B_CODEC_REG_CR3,
     68	JZ4725B_CODEC_REG_AGC1,
     69	JZ4725B_CODEC_REG_AGC2,
     70	JZ4725B_CODEC_REG_AGC3,
     71	JZ4725B_CODEC_REG_AGC4,
     72	JZ4725B_CODEC_REG_AGC5,
     73};
     74
     75#define REG_AICR_CONFIG1_OFFSET		0
     76#define REG_AICR_CONFIG1_MASK		(0xf << REG_AICR_CONFIG1_OFFSET)
     77
     78#define REG_CR1_SB_MICBIAS_OFFSET	7
     79#define REG_CR1_MONO_OFFSET		6
     80#define REG_CR1_DAC_MUTE_OFFSET		5
     81#define REG_CR1_HP_DIS_OFFSET		4
     82#define REG_CR1_DACSEL_OFFSET		3
     83#define REG_CR1_BYPASS_OFFSET		2
     84
     85#define REG_CR2_DAC_DEEMP_OFFSET	7
     86#define REG_CR2_DAC_ADWL_OFFSET		5
     87#define REG_CR2_DAC_ADWL_MASK		(0x3 << REG_CR2_DAC_ADWL_OFFSET)
     88#define REG_CR2_ADC_ADWL_OFFSET		3
     89#define REG_CR2_ADC_ADWL_MASK		(0x3 << REG_CR2_ADC_ADWL_OFFSET)
     90#define REG_CR2_ADC_HPF_OFFSET		2
     91
     92#define REG_CR3_SB_MIC1_OFFSET		7
     93#define REG_CR3_SB_MIC2_OFFSET		6
     94#define REG_CR3_SIDETONE1_OFFSET	5
     95#define REG_CR3_SIDETONE2_OFFSET	4
     96#define REG_CR3_MICDIFF_OFFSET		3
     97#define REG_CR3_MICSTEREO_OFFSET	2
     98#define REG_CR3_INSEL_OFFSET		0
     99#define REG_CR3_INSEL_MASK		(0x3 << REG_CR3_INSEL_OFFSET)
    100
    101#define REG_CCR1_CONFIG4_OFFSET		0
    102#define REG_CCR1_CONFIG4_MASK		(0xf << REG_CCR1_CONFIG4_OFFSET)
    103
    104#define REG_CCR2_DFREQ_OFFSET		4
    105#define REG_CCR2_DFREQ_MASK		(0xf << REG_CCR2_DFREQ_OFFSET)
    106#define REG_CCR2_AFREQ_OFFSET		0
    107#define REG_CCR2_AFREQ_MASK		(0xf << REG_CCR2_AFREQ_OFFSET)
    108
    109#define REG_PMR1_SB_DAC_OFFSET		7
    110#define REG_PMR1_SB_OUT_OFFSET		6
    111#define REG_PMR1_SB_MIX_OFFSET		5
    112#define REG_PMR1_SB_ADC_OFFSET		4
    113#define REG_PMR1_SB_LIN_OFFSET		3
    114#define REG_PMR1_SB_IND_OFFSET		0
    115
    116#define REG_PMR2_LRGI_OFFSET		7
    117#define REG_PMR2_RLGI_OFFSET		6
    118#define REG_PMR2_LRGOD_OFFSET		5
    119#define REG_PMR2_RLGOD_OFFSET		4
    120#define REG_PMR2_GIM_OFFSET		3
    121#define REG_PMR2_SB_MC_OFFSET		2
    122#define REG_PMR2_SB_OFFSET		1
    123#define REG_PMR2_SB_SLEEP_OFFSET	0
    124
    125#define REG_IFR_RAMP_UP_DONE_OFFSET	3
    126#define REG_IFR_RAMP_DOWN_DONE_OFFSET	2
    127
    128#define REG_CGR1_GODL_OFFSET		4
    129#define REG_CGR1_GODL_MASK		(0xf << REG_CGR1_GODL_OFFSET)
    130#define REG_CGR1_GODR_OFFSET		0
    131#define REG_CGR1_GODR_MASK		(0xf << REG_CGR1_GODR_OFFSET)
    132
    133#define REG_CGR2_GO1R_OFFSET		0
    134#define REG_CGR2_GO1R_MASK		(0x1f << REG_CGR2_GO1R_OFFSET)
    135
    136#define REG_CGR3_GO1L_OFFSET		0
    137#define REG_CGR3_GO1L_MASK		(0x1f << REG_CGR3_GO1L_OFFSET)
    138
    139struct jz_icdc {
    140	struct regmap *regmap;
    141	void __iomem *base;
    142	struct clk *clk;
    143};
    144
    145static const SNDRV_CTL_TLVD_DECLARE_DB_LINEAR(jz4725b_dac_tlv, -2250, 0);
    146static const SNDRV_CTL_TLVD_DECLARE_DB_LINEAR(jz4725b_line_tlv, -1500, 600);
    147
    148static const struct snd_kcontrol_new jz4725b_codec_controls[] = {
    149	SOC_DOUBLE_TLV("Master Playback Volume",
    150		       JZ4725B_CODEC_REG_CGR1,
    151		       REG_CGR1_GODL_OFFSET,
    152		       REG_CGR1_GODR_OFFSET,
    153		       0xf, 1, jz4725b_dac_tlv),
    154	SOC_DOUBLE_R_TLV("Master Capture Volume",
    155			 JZ4725B_CODEC_REG_CGR3,
    156			 JZ4725B_CODEC_REG_CGR2,
    157			 REG_CGR2_GO1R_OFFSET,
    158			 0x1f, 1, jz4725b_line_tlv),
    159
    160	SOC_SINGLE("Master Playback Switch", JZ4725B_CODEC_REG_CR1,
    161		   REG_CR1_DAC_MUTE_OFFSET, 1, 1),
    162
    163	SOC_SINGLE("Deemphasize Filter Playback Switch",
    164		   JZ4725B_CODEC_REG_CR2,
    165		   REG_CR2_DAC_DEEMP_OFFSET, 1, 0),
    166
    167	SOC_SINGLE("High-Pass Filter Capture Switch",
    168		   JZ4725B_CODEC_REG_CR2,
    169		   REG_CR2_ADC_HPF_OFFSET, 1, 0),
    170};
    171
    172static const char * const jz4725b_codec_adc_src_texts[] = {
    173	"Mic 1", "Mic 2", "Line In", "Mixer",
    174};
    175static const unsigned int jz4725b_codec_adc_src_values[] = { 0, 1, 2, 3, };
    176static SOC_VALUE_ENUM_SINGLE_DECL(jz4725b_codec_adc_src_enum,
    177				  JZ4725B_CODEC_REG_CR3,
    178				  REG_CR3_INSEL_OFFSET,
    179				  REG_CR3_INSEL_MASK,
    180				  jz4725b_codec_adc_src_texts,
    181				  jz4725b_codec_adc_src_values);
    182static const struct snd_kcontrol_new jz4725b_codec_adc_src_ctrl =
    183			SOC_DAPM_ENUM("Route", jz4725b_codec_adc_src_enum);
    184
    185static const struct snd_kcontrol_new jz4725b_codec_mixer_controls[] = {
    186	SOC_DAPM_SINGLE("Line In Bypass", JZ4725B_CODEC_REG_CR1,
    187			REG_CR1_BYPASS_OFFSET, 1, 0),
    188};
    189
    190static int jz4725b_out_stage_enable(struct snd_soc_dapm_widget *w,
    191				    struct snd_kcontrol *kcontrol,
    192				    int event)
    193{
    194	struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
    195	struct jz_icdc *icdc = snd_soc_component_get_drvdata(codec);
    196	struct regmap *map = icdc->regmap;
    197	unsigned int val;
    198
    199	switch (event) {
    200	case SND_SOC_DAPM_PRE_PMU:
    201		return regmap_clear_bits(map, JZ4725B_CODEC_REG_IFR,
    202					 BIT(REG_IFR_RAMP_UP_DONE_OFFSET));
    203	case SND_SOC_DAPM_POST_PMU:
    204		return regmap_read_poll_timeout(map, JZ4725B_CODEC_REG_IFR,
    205			       val, val & BIT(REG_IFR_RAMP_UP_DONE_OFFSET),
    206			       100000, 500000);
    207	case SND_SOC_DAPM_PRE_PMD:
    208		return regmap_clear_bits(map, JZ4725B_CODEC_REG_IFR,
    209				BIT(REG_IFR_RAMP_DOWN_DONE_OFFSET));
    210	case SND_SOC_DAPM_POST_PMD:
    211		return regmap_read_poll_timeout(map, JZ4725B_CODEC_REG_IFR,
    212			       val, val & BIT(REG_IFR_RAMP_DOWN_DONE_OFFSET),
    213			       100000, 500000);
    214	default:
    215		return -EINVAL;
    216	}
    217}
    218
    219static const struct snd_soc_dapm_widget jz4725b_codec_dapm_widgets[] = {
    220	/* DAC */
    221	SND_SOC_DAPM_DAC("DAC", "Playback",
    222			 JZ4725B_CODEC_REG_PMR1, REG_PMR1_SB_DAC_OFFSET, 1),
    223
    224	/* ADC */
    225	SND_SOC_DAPM_ADC("ADC", "Capture",
    226			 JZ4725B_CODEC_REG_PMR1, REG_PMR1_SB_ADC_OFFSET, 1),
    227
    228	SND_SOC_DAPM_MUX("ADC Source", SND_SOC_NOPM, 0, 0,
    229			 &jz4725b_codec_adc_src_ctrl),
    230
    231	/* Mixer */
    232	SND_SOC_DAPM_MIXER("Mixer", JZ4725B_CODEC_REG_PMR1,
    233			   REG_PMR1_SB_MIX_OFFSET, 1,
    234			   jz4725b_codec_mixer_controls,
    235			   ARRAY_SIZE(jz4725b_codec_mixer_controls)),
    236	SND_SOC_DAPM_MIXER("DAC to Mixer", JZ4725B_CODEC_REG_CR1,
    237			   REG_CR1_DACSEL_OFFSET, 0, NULL, 0),
    238
    239	SND_SOC_DAPM_MIXER("Line In", SND_SOC_NOPM, 0, 0, NULL, 0),
    240	SND_SOC_DAPM_MIXER("HP Out", JZ4725B_CODEC_REG_CR1,
    241			   REG_CR1_HP_DIS_OFFSET, 1, NULL, 0),
    242
    243	SND_SOC_DAPM_MIXER("Mic 1", JZ4725B_CODEC_REG_CR3,
    244			   REG_CR3_SB_MIC1_OFFSET, 1, NULL, 0),
    245	SND_SOC_DAPM_MIXER("Mic 2", JZ4725B_CODEC_REG_CR3,
    246			   REG_CR3_SB_MIC2_OFFSET, 1, NULL, 0),
    247
    248	SND_SOC_DAPM_MIXER_E("Out Stage", JZ4725B_CODEC_REG_PMR1,
    249			     REG_PMR1_SB_OUT_OFFSET, 1, NULL, 0,
    250			     jz4725b_out_stage_enable,
    251			     SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
    252			     SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
    253	SND_SOC_DAPM_MIXER("Mixer to ADC", JZ4725B_CODEC_REG_PMR1,
    254			   REG_PMR1_SB_IND_OFFSET, 1, NULL, 0),
    255
    256	SND_SOC_DAPM_SUPPLY("Mic Bias", JZ4725B_CODEC_REG_CR1,
    257			    REG_CR1_SB_MICBIAS_OFFSET, 1, NULL, 0),
    258
    259	/* Pins */
    260	SND_SOC_DAPM_INPUT("MIC1P"),
    261	SND_SOC_DAPM_INPUT("MIC1N"),
    262	SND_SOC_DAPM_INPUT("MIC2P"),
    263	SND_SOC_DAPM_INPUT("MIC2N"),
    264
    265	SND_SOC_DAPM_INPUT("LLINEIN"),
    266	SND_SOC_DAPM_INPUT("RLINEIN"),
    267
    268	SND_SOC_DAPM_OUTPUT("LHPOUT"),
    269	SND_SOC_DAPM_OUTPUT("RHPOUT"),
    270};
    271
    272static const struct snd_soc_dapm_route jz4725b_codec_dapm_routes[] = {
    273	{"Mic 1", NULL, "MIC1P"},
    274	{"Mic 1", NULL, "MIC1N"},
    275	{"Mic 2", NULL, "MIC2P"},
    276	{"Mic 2", NULL, "MIC2N"},
    277
    278	{"Line In", NULL, "LLINEIN"},
    279	{"Line In", NULL, "RLINEIN"},
    280
    281	{"Mixer", "Line In Bypass", "Line In"},
    282	{"DAC to Mixer", NULL, "DAC"},
    283	{"Mixer", NULL, "DAC to Mixer"},
    284
    285	{"Mixer to ADC", NULL, "Mixer"},
    286	{"ADC Source", "Mixer", "Mixer to ADC"},
    287	{"ADC Source", "Line In", "Line In"},
    288	{"ADC Source", "Mic 1", "Mic 1"},
    289	{"ADC Source", "Mic 2", "Mic 2"},
    290	{"ADC", NULL, "ADC Source"},
    291
    292	{"Out Stage", NULL, "Mixer"},
    293	{"HP Out", NULL, "Out Stage"},
    294	{"LHPOUT", NULL, "HP Out"},
    295	{"RHPOUT", NULL, "HP Out"},
    296};
    297
    298static int jz4725b_codec_set_bias_level(struct snd_soc_component *component,
    299					enum snd_soc_bias_level level)
    300{
    301	struct jz_icdc *icdc = snd_soc_component_get_drvdata(component);
    302	struct regmap *map = icdc->regmap;
    303
    304	switch (level) {
    305	case SND_SOC_BIAS_ON:
    306		regmap_clear_bits(map, JZ4725B_CODEC_REG_PMR2,
    307				  BIT(REG_PMR2_SB_SLEEP_OFFSET));
    308		break;
    309	case SND_SOC_BIAS_PREPARE:
    310		/* Enable sound hardware */
    311		regmap_clear_bits(map, JZ4725B_CODEC_REG_PMR2,
    312				  BIT(REG_PMR2_SB_OFFSET));
    313		msleep(224);
    314		break;
    315	case SND_SOC_BIAS_STANDBY:
    316		regmap_set_bits(map, JZ4725B_CODEC_REG_PMR2,
    317				BIT(REG_PMR2_SB_SLEEP_OFFSET));
    318		break;
    319	case SND_SOC_BIAS_OFF:
    320		regmap_set_bits(map, JZ4725B_CODEC_REG_PMR2,
    321				BIT(REG_PMR2_SB_OFFSET));
    322		break;
    323	}
    324
    325	return 0;
    326}
    327
    328static int jz4725b_codec_dev_probe(struct snd_soc_component *component)
    329{
    330	struct jz_icdc *icdc = snd_soc_component_get_drvdata(component);
    331	struct regmap *map = icdc->regmap;
    332
    333	clk_prepare_enable(icdc->clk);
    334
    335	/* Write CONFIGn (n=1 to 8) bits.
    336	 * The value 0x0f is specified in the datasheet as a requirement.
    337	 */
    338	regmap_write(map, JZ4725B_CODEC_REG_AICR,
    339		     0xf << REG_AICR_CONFIG1_OFFSET);
    340	regmap_write(map, JZ4725B_CODEC_REG_CCR1,
    341		     0x0 << REG_CCR1_CONFIG4_OFFSET);
    342
    343	return 0;
    344}
    345
    346static void jz4725b_codec_dev_remove(struct snd_soc_component *component)
    347{
    348	struct jz_icdc *icdc = snd_soc_component_get_drvdata(component);
    349
    350	clk_disable_unprepare(icdc->clk);
    351}
    352
    353static const struct snd_soc_component_driver jz4725b_codec = {
    354	.probe			= jz4725b_codec_dev_probe,
    355	.remove			= jz4725b_codec_dev_remove,
    356	.set_bias_level		= jz4725b_codec_set_bias_level,
    357	.controls		= jz4725b_codec_controls,
    358	.num_controls		= ARRAY_SIZE(jz4725b_codec_controls),
    359	.dapm_widgets		= jz4725b_codec_dapm_widgets,
    360	.num_dapm_widgets	= ARRAY_SIZE(jz4725b_codec_dapm_widgets),
    361	.dapm_routes		= jz4725b_codec_dapm_routes,
    362	.num_dapm_routes	= ARRAY_SIZE(jz4725b_codec_dapm_routes),
    363	.suspend_bias_off	= 1,
    364	.use_pmdown_time	= 1,
    365};
    366
    367static const unsigned int jz4725b_codec_sample_rates[] = {
    368	96000, 48000, 44100, 32000,
    369	24000, 22050, 16000, 12000,
    370	11025, 9600, 8000,
    371};
    372
    373static int jz4725b_codec_hw_params(struct snd_pcm_substream *substream,
    374	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
    375{
    376	struct jz_icdc *icdc = snd_soc_component_get_drvdata(dai->component);
    377	unsigned int rate, bit_width;
    378
    379	switch (params_format(params)) {
    380	case SNDRV_PCM_FORMAT_S16_LE:
    381		bit_width = 0;
    382		break;
    383	case SNDRV_PCM_FORMAT_S18_3LE:
    384		bit_width = 1;
    385		break;
    386	case SNDRV_PCM_FORMAT_S20_3LE:
    387		bit_width = 2;
    388		break;
    389	case SNDRV_PCM_FORMAT_S24_3LE:
    390		bit_width = 3;
    391		break;
    392	default:
    393		return -EINVAL;
    394	}
    395
    396	for (rate = 0; rate < ARRAY_SIZE(jz4725b_codec_sample_rates); rate++) {
    397		if (jz4725b_codec_sample_rates[rate] == params_rate(params))
    398			break;
    399	}
    400
    401	if (rate == ARRAY_SIZE(jz4725b_codec_sample_rates))
    402		return -EINVAL;
    403
    404	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
    405		regmap_update_bits(icdc->regmap,
    406				   JZ4725B_CODEC_REG_CR2,
    407				   REG_CR2_DAC_ADWL_MASK,
    408				   bit_width << REG_CR2_DAC_ADWL_OFFSET);
    409
    410		regmap_update_bits(icdc->regmap,
    411				   JZ4725B_CODEC_REG_CCR2,
    412				   REG_CCR2_DFREQ_MASK,
    413				   rate << REG_CCR2_DFREQ_OFFSET);
    414	} else {
    415		regmap_update_bits(icdc->regmap,
    416				   JZ4725B_CODEC_REG_CR2,
    417				   REG_CR2_ADC_ADWL_MASK,
    418				   bit_width << REG_CR2_ADC_ADWL_OFFSET);
    419
    420		regmap_update_bits(icdc->regmap,
    421				   JZ4725B_CODEC_REG_CCR2,
    422				   REG_CCR2_AFREQ_MASK,
    423				   rate << REG_CCR2_AFREQ_OFFSET);
    424	}
    425
    426	return 0;
    427}
    428
    429static const struct snd_soc_dai_ops jz4725b_codec_dai_ops = {
    430	.hw_params = jz4725b_codec_hw_params,
    431};
    432
    433#define JZ_ICDC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S18_3LE | \
    434			 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE)
    435
    436static struct snd_soc_dai_driver jz4725b_codec_dai = {
    437	.name = "jz4725b-hifi",
    438	.playback = {
    439		.stream_name = "Playback",
    440		.channels_min = 2,
    441		.channels_max = 2,
    442		.rates = SNDRV_PCM_RATE_8000_96000,
    443		.formats = JZ_ICDC_FORMATS,
    444	},
    445	.capture = {
    446		.stream_name = "Capture",
    447		.channels_min = 2,
    448		.channels_max = 2,
    449		.rates = SNDRV_PCM_RATE_8000_96000,
    450		.formats = JZ_ICDC_FORMATS,
    451	},
    452	.ops = &jz4725b_codec_dai_ops,
    453};
    454
    455static bool jz4725b_codec_volatile(struct device *dev, unsigned int reg)
    456{
    457	return reg == JZ4725B_CODEC_REG_IFR;
    458}
    459
    460static bool jz4725b_codec_can_access_reg(struct device *dev, unsigned int reg)
    461{
    462	return (reg != JZ4725B_CODEC_REG_TR1) && (reg != JZ4725B_CODEC_REG_TR2);
    463}
    464
    465static int jz4725b_codec_io_wait(struct jz_icdc *icdc)
    466{
    467	u32 reg;
    468
    469	return readl_poll_timeout(icdc->base + ICDC_RGADW_OFFSET, reg,
    470				  !(reg & ICDC_RGADW_RGWR), 1000, 10000);
    471}
    472
    473static int jz4725b_codec_reg_read(void *context, unsigned int reg,
    474				  unsigned int *val)
    475{
    476	struct jz_icdc *icdc = context;
    477	unsigned int i;
    478	u32 tmp;
    479	int ret;
    480
    481	ret = jz4725b_codec_io_wait(icdc);
    482	if (ret)
    483		return ret;
    484
    485	tmp = readl(icdc->base + ICDC_RGADW_OFFSET);
    486	tmp = (tmp & ~ICDC_RGADW_RGADDR_MASK)
    487	    | (reg << ICDC_RGADW_RGADDR_OFFSET);
    488	writel(tmp, icdc->base + ICDC_RGADW_OFFSET);
    489
    490	/* wait 6+ cycles */
    491	for (i = 0; i < 6; i++)
    492		*val = readl(icdc->base + ICDC_RGDATA_OFFSET) &
    493			ICDC_RGDATA_RGDOUT_MASK;
    494
    495	return 0;
    496}
    497
    498static int jz4725b_codec_reg_write(void *context, unsigned int reg,
    499				   unsigned int val)
    500{
    501	struct jz_icdc *icdc = context;
    502	int ret;
    503
    504	ret = jz4725b_codec_io_wait(icdc);
    505	if (ret)
    506		return ret;
    507
    508	writel(ICDC_RGADW_RGWR | (reg << ICDC_RGADW_RGADDR_OFFSET) | val,
    509			icdc->base + ICDC_RGADW_OFFSET);
    510
    511	ret = jz4725b_codec_io_wait(icdc);
    512	if (ret)
    513		return ret;
    514
    515	return 0;
    516}
    517
    518static const u8 jz4725b_codec_reg_defaults[] = {
    519	0x0c, 0xaa, 0x78, 0x00, 0x00, 0xff, 0x03, 0x51,
    520	0x3f, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04,
    521	0x04, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0xc0, 0x34,
    522	0x07, 0x44, 0x1f, 0x00,
    523};
    524
    525static const struct regmap_config jz4725b_codec_regmap_config = {
    526	.reg_bits = 7,
    527	.val_bits = 8,
    528
    529	.max_register = JZ4725B_CODEC_REG_AGC5,
    530	.volatile_reg = jz4725b_codec_volatile,
    531	.readable_reg = jz4725b_codec_can_access_reg,
    532	.writeable_reg = jz4725b_codec_can_access_reg,
    533
    534	.reg_read = jz4725b_codec_reg_read,
    535	.reg_write = jz4725b_codec_reg_write,
    536
    537	.reg_defaults_raw = jz4725b_codec_reg_defaults,
    538	.num_reg_defaults_raw = ARRAY_SIZE(jz4725b_codec_reg_defaults),
    539	.cache_type = REGCACHE_FLAT,
    540};
    541
    542static int jz4725b_codec_probe(struct platform_device *pdev)
    543{
    544	struct device *dev = &pdev->dev;
    545	struct jz_icdc *icdc;
    546	int ret;
    547
    548	icdc = devm_kzalloc(dev, sizeof(*icdc), GFP_KERNEL);
    549	if (!icdc)
    550		return -ENOMEM;
    551
    552	icdc->base = devm_platform_ioremap_resource(pdev, 0);
    553	if (IS_ERR(icdc->base))
    554		return PTR_ERR(icdc->base);
    555
    556	icdc->regmap = devm_regmap_init(dev, NULL, icdc,
    557					&jz4725b_codec_regmap_config);
    558	if (IS_ERR(icdc->regmap))
    559		return PTR_ERR(icdc->regmap);
    560
    561	icdc->clk = devm_clk_get(&pdev->dev, "aic");
    562	if (IS_ERR(icdc->clk))
    563		return PTR_ERR(icdc->clk);
    564
    565	platform_set_drvdata(pdev, icdc);
    566
    567	ret = devm_snd_soc_register_component(dev, &jz4725b_codec,
    568					      &jz4725b_codec_dai, 1);
    569	if (ret)
    570		dev_err(dev, "Failed to register codec\n");
    571
    572	return ret;
    573}
    574
    575static const struct of_device_id jz4725b_codec_of_matches[] = {
    576	{ .compatible = "ingenic,jz4725b-codec", },
    577	{ }
    578};
    579MODULE_DEVICE_TABLE(of, jz4725b_codec_of_matches);
    580
    581static struct platform_driver jz4725b_codec_driver = {
    582	.probe = jz4725b_codec_probe,
    583	.driver = {
    584		.name = "jz4725b-codec",
    585		.of_match_table = jz4725b_codec_of_matches,
    586	},
    587};
    588module_platform_driver(jz4725b_codec_driver);
    589
    590MODULE_DESCRIPTION("JZ4725B SoC internal codec driver");
    591MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
    592MODULE_LICENSE("GPL v2");