[RFC v2 0/5] CI20 sound

14 views
Skip to first unread message

H. Nikolaus Schaller

unread,
May 2, 2021, 5:17:05 AM5/2/21
to pa...@boddie.org.uk, mips-creat...@googlegroups.com, letux-...@openphoenux.org, maa...@treewalker.org, H. Nikolaus Schaller
Updates v2:
* replace jz4780 codec based on jz4770 by code specific for the jz4780 from the 3.18 kernel
* add ci20-audio from the 3.18 kernel to replace simple-audio-card because the CI20 needs some special setup
for mic type detecion and HDMI audio
* enable kernel module configs
* debugged to create an audio card (which does not yet fully work)

Notes:
- not cleaned
- not checkpatched
- no YAML
- needs CI HDMI solution as basis
- tested on letux-5.12 kernel

It now basically works:

root@letux:~# aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: ci20 [ci20], device 0: headphones 100200a4.audio-codec-0 [headphones 100200a4.audio-codec-0]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 0: ci20 [ci20], device 1: hdmi i2s-hifi-1 [hdmi i2s-hifi-1]
Subdevices: 1/1
Subdevice #0: subdevice #0
root@letux:~# amixer
Simple mixer control Master,0
Capabilities: pvolume cvolume pswitch pswitch-joined
Playback channels: Front Left - Front Right
Capture channels: Front Left - Front Right
Limits: Playback 0 - 31 Capture 0 - 63
Front Left: Playback 25 [81%] [on] Capture 51 [81%]
Front Right: Playback 25 [81%] [on] Capture 51 [81%]
Simple mixer control Headphone,0
Capabilities: pvolume pswitch pswitch-joined
Playback channels: Front Left - Front Right
Limits: Playback 0 - 31
Mono:
Front Left: Playback 22 [71%] [on]
Front Right: Playback 22 [71%] [on]
Simple mixer control Mic,0
Capabilities: cvolume cvolume-joined cswitch cswitch-joined
Capture channels: Mono
Limits: Capture 0 - 7
Mono: Capture 0 [0%] [off]
Simple mixer control ADC,0
Capabilities: cenum
Items: AIP1 AIP2
Item0: AIP1
root@letux:~#

Except that there is no sound but an I/O error and the aplay command takes longer than expected:

root@letux:~# time aplay /usr/share/sounds/alsa/Front_Center.wav
Playing WAVE /usr/share/sounds/alsa/Front_Center.wav : Signed 16 bit Little Endian, Rate 48000 Hz, Mono
aplay: pcm_write:1939: write error: Input/output error

real 0m13.141s
user 0m0.021s
sys 0m0.012s
root@letux:~#

There is one boot-warning:

[ 4.089501] jz4780-codec 100200a4.audio-codec: No cache used with register defaults set!

But that just confirms that we have

.cache_type = REGCACHE_NONE,

For the regmap of the jz4780-codec driver.

---

RFC V1 2021-04-30 18:55:28:
defines:
* DTS nodes (some uncertainty about clocks and interrupts) for jz4780, ci20
* adds a jz4780 codec driver based on jz4770 driver
* start to adjust register offsets
* enable in ci20_defconfig

base: v5.12


H. Nikolaus Schaller (5):
ASoC: codecs: Add jz4780-codec driver
ASoC: JZ4740: Add CI20 board driver
MIPS: DTS: jz4780: add definition for for i2s and codec
MIPS: DTS: CI20: provide sound
MIPS: configs: ci20: enable SOUND and I2S/Codec

arch/mips/boot/dts/ingenic/ci20.dts | 40 ++
arch/mips/boot/dts/ingenic/jz4780.dtsi | 33 ++
arch/mips/configs/ci20_defconfig | 10 +
sound/soc/codecs/Kconfig | 13 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/jz4780.c | 635 +++++++++++++++++++++++++
sound/soc/jz4740/Kconfig | 7 +
sound/soc/jz4740/Makefile | 1 +
sound/soc/jz4740/ci20-audio.c | 317 ++++++++++++
9 files changed, 1058 insertions(+)
create mode 100644 sound/soc/codecs/jz4780.c
create mode 100644 sound/soc/jz4740/ci20-audio.c

--
2.26.2

H. Nikolaus Schaller

unread,
May 2, 2021, 5:18:24 AM5/2/21
to pa...@boddie.org.uk, mips-creat...@googlegroups.com, letux-...@openphoenux.org, maa...@treewalker.org, H. Nikolaus Schaller, Paul Burton
The CI20 provides a sound card with two devices. One for the headset/mic jack
and one for HDMI. It also provides two jack detect input event sources
(HDMI is always plugged). Special driver code triggers the mic polarity
detector on plugin-events.

This driver is based on the old 3.18 kernel by Paul Burton <paul....@imgtec.com>

I have modified the code to compile on v5.12.

root@letux:~# aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: ci20 [ci20], device 0: headphones 100200a4.audio-codec-0 [headphones 100200a4.audio-codec-0]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 0: ci20 [ci20], device 1: hdmi i2s-hifi-1 [hdmi i2s-hifi-1]
Subdevices: 1/1
Subdevice #0: subdevice #0
root@letux:~#

FIXME:
* convert to gpiod
* provide hdmi-codec through DT
* provide YAML

Signed-off-by: Paul Burton <paul....@imgtec.com>
Co-Authored-by: Paul Burton <paul....@imgtec.com>
Signed-off-by: H. Nikolaus Schaller <h...@goldelico.com>
---
sound/soc/jz4740/Kconfig | 7 +
sound/soc/jz4740/Makefile | 1 +
sound/soc/jz4740/ci20-audio.c | 317 ++++++++++++++++++++++++++++++++++
3 files changed, 325 insertions(+)
create mode 100644 sound/soc/jz4740/ci20-audio.c

diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig
index 29144720cb622..fcb3e36c90bd6 100644
--- a/sound/soc/jz4740/Kconfig
+++ b/sound/soc/jz4740/Kconfig
@@ -7,3 +7,10 @@ config SND_JZ4740_SOC_I2S
help
Say Y if you want to use I2S protocol and I2S codec on Ingenic JZ4740
based boards.
+
+config SND_JZ4780_SOC_CI20
+ tristate "SoC Audio support for ci20"
+ depends on SND_JZ4740_SOC_I2S && JZ4780_CI20
+ select SND_SOC_JZ4780_CODEC
+ help
+ Say Y if you want to add support for ASoC audio on the ci20.
diff --git a/sound/soc/jz4740/Makefile b/sound/soc/jz4740/Makefile
index f8701c9b09fe2..e4dd222a33d71 100644
--- a/sound/soc/jz4740/Makefile
+++ b/sound/soc/jz4740/Makefile
@@ -5,3 +5,4 @@
snd-soc-jz4740-i2s-objs := jz4740-i2s.o

obj-$(CONFIG_SND_JZ4740_SOC_I2S) += snd-soc-jz4740-i2s.o
+obj-$(CONFIG_SND_JZ4780_SOC_CI20) += ci20-audio.o
diff --git a/sound/soc/jz4740/ci20-audio.c b/sound/soc/jz4740/ci20-audio.c
new file mode 100644
index 0000000000000..a4a7bdd8e4188
--- /dev/null
+++ b/sound/soc/jz4740/ci20-audio.c
@@ -0,0 +1,317 @@
+/*
+ * CI20 ASoC driver
+ *
+ * Copyright (c) 2013 Imagination Technologies
+ * Author: Paul Burton <paul....@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+#include <sound/jack.h>
+#include <sound/soc.h>
+
+/* FIXME: convert to gpiod */
+static int hp_mute_gpio;
+static int hp_mic_sw_en_gpio;
+
+static struct snd_soc_jack ci20_hp_jack;
+static struct snd_soc_jack ci20_hdmi_jack;
+
+static struct snd_soc_jack_pin ci20_hp_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+static int ci20_hp_jack_status_check(void *data);
+
+static struct snd_soc_jack_gpio ci20_hp_jack_gpio = {
+ .name = "headphone detect",
+ .report = SND_JACK_HEADPHONE,
+ .debounce_time = 200,
+ .invert = 1,
+ .jack_status_check = ci20_hp_jack_status_check,
+};
+
+static int ci20_hp_jack_status_check(void *data)
+{
+ int enable;
+
+ enable = !gpio_get_value_cansleep(ci20_hp_jack_gpio.gpio);
+
+ /*
+ * The headset type detection switch requires a rising edge on its
+ * enable pin to trigger the detection sequence.
+ */
+ if (enable) {
+ gpio_set_value_cansleep(hp_mic_sw_en_gpio, 1);
+ return SND_JACK_HEADPHONE;
+ } else {
+ gpio_set_value_cansleep(hp_mic_sw_en_gpio, 0);
+ return 0;
+ }
+}
+
+static int ci20_hp_event(struct snd_soc_dapm_widget *widget,
+ struct snd_kcontrol *ctrl, int event)
+{
+ gpio_set_value(hp_mute_gpio, !!SND_SOC_DAPM_EVENT_OFF(event));
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget ci20_widgets[] = {
+ SND_SOC_DAPM_MIC("Mic", NULL),
+ SND_SOC_DAPM_HP("Headphone Jack", ci20_hp_event),
+ SND_SOC_DAPM_LINE("HDMI", NULL),
+};
+
+static const struct snd_soc_dapm_route ci20_routes[] = {
+ {"Mic", NULL, "AIP2"},
+ {"Headphone Jack", NULL, "AOHPL"},
+ {"Headphone Jack", NULL, "AOHPR"},
+ {"HDMI", NULL, "TX"},
+};
+
+static int ci20_audio_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ unsigned int dai_fmt = rtd->dai_link->dai_fmt;
+ int mclk, ret;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 48000:
+ case 64000:
+ case 96000:
+ mclk = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ mclk = 11289600;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "failed to set cpu_dai fmt.\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "failed to set cpu_dai sysclk.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ci20_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *codec = dai->component;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ snd_soc_card_jack_new(card, "Headphone Jack", SND_JACK_HEADPHONE,
+ &ci20_hp_jack, ci20_hp_jack_pins, ARRAY_SIZE(ci20_hp_jack_pins));
+ snd_soc_jack_add_gpios(&ci20_hp_jack, 1, &ci20_hp_jack_gpio);
+
+ snd_soc_dapm_nc_pin(dapm, "AIP1");
+ snd_soc_dapm_nc_pin(dapm, "AIP3");
+ snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static int ci20_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *codec = dai->component;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ snd_soc_dapm_enable_pin(dapm, "HDMI");
+
+ /* Enable headphone jack detection */
+ snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT,
+ &ci20_hdmi_jack, NULL, 0);
+
+ /* Jack is connected (it just is) */
+ snd_soc_jack_report(&ci20_hdmi_jack, SND_JACK_LINEOUT, SND_JACK_LINEOUT);
+ return 0;
+}
+
+static struct snd_soc_ops ci20_audio_dai_ops = {
+ .hw_params = ci20_audio_hw_params,
+};
+
+#define CI20_DAIFMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF \
+ | SND_SOC_DAIFMT_CBM_CFM)
+
+static struct snd_soc_dai_link_component dai_link0_cpus[] = {
+ { .name = "jz4780-i2s", },
+};
+
+static struct snd_soc_dai_link_component dai_link0_platforms[] = {
+ { .name = "jz4780-i2s", },
+};
+
+static struct snd_soc_dai_link_component dai_link0_codecs[] = {
+ { .name = "jz4780-codec", .dai_name = "jz4780-hifi", },
+};
+
+static struct snd_soc_dai_link_component dai_link1_cpus[] = {
+ { .name = "jz4780-i2s", },
+};
+
+static struct snd_soc_dai_link_component dai_link1_platforms[] = {
+ { .name = "jz4780-i2s", },
+};
+
+static struct snd_soc_dai_link_component dai_link1_codecs[] = {
+ { .name = /* "dw-hdmi-audio" */ "hdmi-audio-codec.3.auto", .dai_name = /* "dw-hdmi-hifi" */ "i2s-hifi", },
+};
+
+static struct snd_soc_dai_link ci20_dai_link[] = {
+ {
+ .name = "ci20",
+ .stream_name = "headphones",
+ .cpus = dai_link0_cpus,
+ .num_cpus = ARRAY_SIZE(dai_link0_cpus),
+ .platforms = dai_link0_platforms,
+ .num_platforms = ARRAY_SIZE(dai_link0_platforms),
+ .codecs = dai_link0_codecs,
+ .num_codecs = ARRAY_SIZE(dai_link0_codecs),
+ .init = ci20_init,
+ .ops = &ci20_audio_dai_ops,
+ .dai_fmt = CI20_DAIFMT,
+ },
+ {
+ .name = "ci20 HDMI",
+ .stream_name = "hdmi",
+ .cpus = dai_link1_cpus,
+ .num_cpus = ARRAY_SIZE(dai_link1_cpus),
+ .platforms = dai_link1_platforms,
+ .num_platforms = ARRAY_SIZE(dai_link1_platforms),
+ .codecs = dai_link1_codecs,
+ .num_codecs = ARRAY_SIZE(dai_link1_codecs),
+ .init = ci20_hdmi_init,
+ .ops = &ci20_audio_dai_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS,
+ }
+};
+
+static struct snd_soc_card ci20_audio_card = {
+ .name = "ci20",
+ .dai_link = ci20_dai_link,
+ .num_links = ARRAY_SIZE(ci20_dai_link),
+
+ .dapm_widgets = ci20_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ci20_widgets),
+ .dapm_routes = ci20_routes,
+ .num_dapm_routes = ARRAY_SIZE(ci20_routes),
+};
+
+static const struct of_device_id ingenic_asoc_ci20_dt_ids[] = {
+ { .compatible = "ingenic,ci20-audio", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ingenic_asoc_ci20_dt_ids);
+
+static int ingenic_asoc_ci20_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ struct snd_soc_card *card = &ci20_audio_card;
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *codec, *i2s;
+
+ card->dev = &pdev->dev;
+
+ i2s = of_parse_phandle(np, "ingenic,i2s-controller", 0);
+ codec = of_parse_phandle(np, "ingenic,codec", 0);
+
+ if (!i2s || !codec) {
+ dev_warn(&pdev->dev,
+ "Phandle not found for i2s/codecs, using defaults\n");
+ } else {
+ dev_dbg(&pdev->dev, "Setting dai_link parameters\n");
+ ci20_dai_link[0].cpus[0].of_node = i2s;
+ ci20_dai_link[0].cpus[0].name = NULL;
+ ci20_dai_link[1].cpus[0].of_node = i2s;
+ ci20_dai_link[1].cpus[0].name = NULL;
+ ci20_dai_link[0].platforms[0].of_node = i2s;
+ ci20_dai_link[0].platforms[0].name = NULL;
+ ci20_dai_link[1].platforms[0].of_node = i2s;
+ ci20_dai_link[1].platforms[0].name = NULL;
+ ci20_dai_link[0].codecs[0].of_node = codec;
+ ci20_dai_link[0].codecs[0].name = NULL;
+ }
+
+/* FIXME: convert to gpiod and add error handling */
+
+ ci20_hp_jack_gpio.gpio = of_get_named_gpio(np, "ingenic,hp-det-gpio", 0);
+ hp_mute_gpio = of_get_named_gpio(np, "ingenic,hp-mute-gpio", 0);
+ hp_mic_sw_en_gpio = of_get_named_gpio(np, "ingenic,mic-detect-gpio", 0);
+
+ gpio_direction_output(hp_mute_gpio, 1);
+ gpio_direction_output(hp_mic_sw_en_gpio, 0);
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if ((ret) && (ret != -EPROBE_DEFER))
+ dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret);
+ return ret;
+}
+
+static int ingenic_asoc_ci20_remove(struct platform_device *pdev)
+{
+ snd_soc_jack_free_gpios(&ci20_hp_jack, 1, &ci20_hp_jack_gpio);
+
+ return 0;
+}
+
+static struct platform_driver ingenic_ci20_audio_driver = {
+ .driver = {
+ .name = "ingenic-ci20-audio",
+ .owner = THIS_MODULE,
+ .of_match_table = ingenic_asoc_ci20_dt_ids,
+ },
+ .probe = ingenic_asoc_ci20_probe,
+ .remove = ingenic_asoc_ci20_remove,
+};
+
+module_platform_driver(ingenic_ci20_audio_driver);
+
+MODULE_AUTHOR("Paul Burton <paul....@imgtec.com>");
+MODULE_DESCRIPTION("ci20/JZ4780 ASoC driver");
+MODULE_LICENSE("GPL");
--
2.26.2

H. Nikolaus Schaller

unread,
May 2, 2021, 5:20:11 AM5/2/21
to pa...@boddie.org.uk, mips-creat...@googlegroups.com, letux-...@openphoenux.org, maa...@treewalker.org, H. Nikolaus Schaller
define that we have a ingenic,ci20-audio system.

Also describe which gpios are used for headset and microphone
detect.

Signed-off-by: H. Nikolaus Schaller <h...@goldelico.com>
---
arch/mips/boot/dts/ingenic/ci20.dts | 40 +++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)

diff --git a/arch/mips/boot/dts/ingenic/ci20.dts b/arch/mips/boot/dts/ingenic/ci20.dts
index f39e137370614..50f0860803600 100644
--- a/arch/mips/boot/dts/ingenic/ci20.dts
+++ b/arch/mips/boot/dts/ingenic/ci20.dts
@@ -67,6 +67,46 @@ led3 {
};
};

+ sound {
+ compatible = "ingenic,ci20-audio";
+
+ ingenic,i2s-controller = <&i2s0>;
+ ingenic,codec = <&codec>;
+
+ ingenic,hp-det-gpio = <&gpe 7 GPIO_ACTIVE_HIGH>; /* 135 */
+ ingenic,hp-mute-gpio = <&gpd 13 GPIO_ACTIVE_HIGH>; /* 109 */
+ ingenic,mic-detect-gpio = <&gpf 14 GPIO_ACTIVE_HIGH>; /* 174 */
+
+#if SIMPLE_AUDIO_CARD
+ simple-audio-card,name = "CI20";
+ simple-audio-card,format = "i2s";
+
+ simple-audio-card,widgets =
+ "Speaker", "Speaker",
+ "Microphone", "Mic";
+ simple-audio-card,routing =
+ "MIC", "Mic",
+ "Speaker", "OUTL",
+ "Speaker", "OUTR",
+ "INL", "LOUT",
+ "INR", "ROUT";
+
+ simple-audio-card,bitclock-master = <&dai_codec>;
+ simple-audio-card,frame-master = <&dai_codec>;
+
+ simple-audio-card,hp-det-gpio = <&gpe 7 GPIO_ACTIVE_LOW>;
+ /* can't handle mic detect gpio */
+
+ dai_cpu: simple-audio-card,cpu {
+ sound-dai = <&i2s0>;
+ };
+
+ dai_codec: simple-audio-card,codec {
+ sound-dai = <&codec>;
+ };
+#endif
+ };
+
eth0_power: fixedregulator@0 {
compatible = "regulator-fixed";

--
2.26.2

H. Nikolaus Schaller

unread,
May 2, 2021, 5:24:59 AM5/2/21
to Paul Boddie, MIPS Creator CI20 Development, Discussions about the Letux Kernel, Maarten ter Huurne
BTW: all effects (I/O-error and no sound) and the extra long time seem to be symptoms of a problem
of the jz4740-i2s driver. I have tried with both variants, the upstream one and a variant which I had
patched to be able to access the Codec of the Alpha 400.

The upstream version needs 23 seconds while the patched one just needs these 13 seconds as logged above.

This indicates that there is the same codec communication problem as with the jz4730+codec...

So I think we are narrowing down the issues.

BTW: head set detect on CI20 works after finding the right headset (it is 3.5mm and not 2.5mm
as I originally thought).

BR,
Nikolaus

H. Nikolaus Schaller

unread,
May 2, 2021, 5:26:39 AM5/2/21
to pa...@boddie.org.uk, mips-creat...@googlegroups.com, letux-...@openphoenux.org, maa...@treewalker.org, H. Nikolaus Schaller, Paul Burton
The jz4780 SoC has a built-in audio codec.

This driver is based on the old 3.18 kernel by Paul Burton.

I have modified the code to compile on v5.12.

FIXME:
* use REGCACHE_FLAT but that needs volatile/readable defined
* remove, suspend
* provide YAML
* add more controls for additional codec features

Signed-off-by: Paul Burton <paul....@imgtec.com>
Co-Authored-by: Paul Burton <paul....@imgtec.com>
Signed-off-by: H. Nikolaus Schaller <h...@goldelico.com>
---
sound/soc/codecs/Kconfig | 13 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/jz4780.c | 635 ++++++++++++++++++++++++++++++++++++++
3 files changed, 650 insertions(+)
create mode 100644 sound/soc/codecs/jz4780.c

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index cf5e6074ff1c0..043450db8a3b4 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -106,6 +106,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_JZ4725B_CODEC
imply SND_SOC_JZ4760_CODEC
imply SND_SOC_JZ4770_CODEC
+ imply SND_SOC_JZ4780_CODEC
imply SND_SOC_LM4857
imply SND_SOC_LM49453
imply SND_SOC_LOCHNAGAR_SC
@@ -760,6 +761,18 @@ config SND_SOC_JZ4770_CODEC
This driver can also be built as a module. If so, the module
will be called snd-soc-jz4770-codec.

+config SND_SOC_JZ4780_CODEC
+ depends on MACH_INGENIC || COMPILE_TEST
+ depends on OF
+ select REGMAP
+ tristate "Ingenic JZ4780 internal CODEC"
+ help
+ Enable support for the internal CODEC found in the JZ4780 SoC
+ from Ingenic.
+
+ This driver can also be built as a module. If so, the module
+ will be called snd-soc-jz4780-codec.
+
config SND_SOC_L3
tristate

diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index edcbcf5633c46..9aa99bf7b55a9 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -108,6 +108,7 @@ snd-soc-jz4740-codec-objs := jz4740.o
snd-soc-jz4725b-codec-objs := jz4725b.o
snd-soc-jz4760-codec-objs := jz4760.o
snd-soc-jz4770-codec-objs := jz4770.o
+snd-soc-jz4780-codec-objs := jz4780.o
snd-soc-l3-objs := l3.o
snd-soc-lm4857-objs := lm4857.o
snd-soc-lm49453-objs := lm49453.o
@@ -428,6 +429,7 @@ obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
obj-$(CONFIG_SND_SOC_JZ4725B_CODEC) += snd-soc-jz4725b-codec.o
obj-$(CONFIG_SND_SOC_JZ4760_CODEC) += snd-soc-jz4760-codec.o
obj-$(CONFIG_SND_SOC_JZ4770_CODEC) += snd-soc-jz4770-codec.o
+obj-$(CONFIG_SND_SOC_JZ4780_CODEC) += snd-soc-jz4780-codec.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o
obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o
diff --git a/sound/soc/codecs/jz4780.c b/sound/soc/codecs/jz4780.c
new file mode 100644
index 0000000000000..c0be29214a94b
--- /dev/null
+++ b/sound/soc/codecs/jz4780.c
@@ -0,0 +1,635 @@
+/*
+ * Ingenic JZ4780 ASoC codec driver
+ *
+ * Copyright (c) 2013 Imagination Technologies
+ * Author: Paul Burton <paul....@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#define REG_SR 0x00
+#define REG_SR2 0x01
+#define REG_MR 0x07
+#define REG_AICR_DAC 0x08
+#define REG_AICR_ADC 0x09
+#define REG_CR_LO 0x0B
+#define REG_CR_HP 0x0D
+#define REG_CR_DMIC 0x10
+#define REG_CR_MIC1 0x11
+#define REG_CR_MIC2 0x12
+#define REG_CR_LI1 0x13
+#define REG_CR_LI2 0x14
+#define REG_CR_DAC 0x17
+#define REG_CR_ADC 0x18
+#define REG_CR_MIX 0x19
+#define REG_DR_MIX 0x1A
+#define REG_CR_VIC 0x1B
+#define REG_CR_CK 0x1C
+#define REG_FCR_DAC 0x1D
+#define REG_FCR_ADC 0x20
+#define REG_CR_TIMER_MSB 0x21
+#define REG_CR_TIMER_LSB 0x22
+#define REG_ICR 0x23
+#define REG_IMR 0x24
+#define REG_IFR 0x25
+#define REG_IMR2 0x26
+#define REG_IFR2 0x27
+#define REG_GCR_HPL 0x28
+#define REG_GCR_HPR 0x29
+#define REG_GCR_LIBYL 0x2A
+#define REG_GCR_LIBYR 0x2B
+#define REG_GCR_DACL 0x2C
+#define REG_GCR_DACR 0x2D
+#define REG_GCR_MIC1 0x2E
+#define REG_GCR_MIC2 0x2F
+#define REG_GCR_ADCL 0x30
+#define REG_GCR_ADCR 0x31
+#define REG_GCR_MIXDACL 0x34
+#define REG_GCR_MIXDACR 0x35
+#define REG_GCR_MIXADCL 0x36
+#define REG_GCR_MIXADCR 0x37
+#define REG_CR_ADC_AGC 0x3A
+#define REG_DR_ADC_AGC 0x3B
+
+#define REG_DMIXER 0x100
+#define REG_DMIX_0 REG_DMIXER | 0x0
+#define REG_DMIX_1 REG_DMIXER | 0x1
+#define REG_DMIX_2 REG_DMIXER | 0x2
+#define REG_DMIX_3 REG_DMIXER | 0x3
+
+#define REG_RGADW_OFF 0
+#define REG_RGDATA_OFF 4
+#define REG_RGADW_RGADDR_SHIFT 8
+#define REG_RGADW_RGADDR_MASK 0x7F
+#define REG_RGADW_RGDIN_MASK 0xFF
+#define REG_RGADW_RGWR BIT(16)
+#define REG_RGADW_ICRST BIT(31)
+
+#define REG_CR_MIC1_MICSTEREO_MASK BIT(7)
+#define REG_CR_MIC1_MICSTEREO_MONO 0
+#define REG_CR_MIC1_MICSTEREO_STEREO 1
+#define REG_CR_MIC1_MIC1_SEL_MASK BIT(1)
+#define REG_CR_MIC1_MIC1_SEL_AIP2 1
+
+#define REG_CR_ADC_ADC_LEFT_ONLY_MASK BIT(5)
+#define REG_CR_ADC_ADC_LEFT_ONLY_RCH_ACTIVE 0
+#define REG_CR_ADC_ADC_LEFT_ONLY_RCH_INACTIVE 1
+#define REG_CR_ADC_ADC_IN_SEL_MASK 0x3
+#define REG_CR_ADC_ADC_IN_SEL_AIN1 0
+
+#define REG_CR_DAC_DAC_MUTE_MASK BIT(7)
+#define REG_CR_DAC_DAC_MUTE_SHIFT 7
+#define REG_CR_DAC_DAC_MUTE_INACTIVE 0
+#define REG_CR_DAC_DAC_MUTE_SOFT_MUTE 1
+
+#define REG_AICR_DAC_DAC_ADWL_MASK 0xC0
+#define REG_AICR_DAC_DAC_ADWL_SHIFT 6
+#define REG_AICR_DAC_AUDIOIF_MASK 0x3
+#define REG_AICR_DAC_AUDIOIF_I2S 0x3
+
+#define REG_AICR_ADC_ADC_ADWL_MASK 0xC0
+#define REG_AICR_ADC_ADC_ADWL_SHIFT 6
+#define REG_AICR_ADC_AUDIOIF_MASK 0x3
+#define REG_AICR_ADC_AUDIOIF_I2S 0x3
+
+#define REG_FCR_DAC_MASK 0xF
+
+#define REG_CR_VIC_SB_MASK BIT(0)
+#define REG_CR_VIC_SB_NORMAL_MODE 0
+#define REG_CR_VIC_SB_POWERDOWN_MODE 1
+#define REG_CR_VIC_SB_SLEEP_MASK BIT(1)
+#define REG_CR_VIC_SB_SLEEP_NORMAL_MODE 0
+#define REG_CR_VIC_SB_SLEEP_SLEEP_MODE 0x2
+
+#define REG_CR_MIX_MIX_EN_MASK BIT(7)
+#define REG_CR_MIX_MIX_EN_OFF 0
+#define REG_CR_MIX_MIX_EN_ENABLED 1
+#define REG_CR_MIX_MIX_LOAD_MASK BIT(6)
+#define REG_CR_MIX_MIX_LOAD_READ 0
+#define REG_CR_MIX_MIX_LOAD_WRITE 0x40
+#define REG_CR_MIX_DAC_MIX_MASK 0x3
+
+
+static struct reg_default jz4780_codec_reg_defaults[] = {
+ { REG_AICR_DAC, 0xd3 },
+ { REG_AICR_ADC, 0xd3 },
+ { REG_CR_LO, 0x90 },
+ { REG_CR_HP, 0x90 },
+ { REG_CR_MIC1, 0xb0 },
+ { REG_CR_MIC2, 0x30 },
+ { REG_CR_LI1, 0x10 },
+ { REG_CR_LI2, 0x10 },
+ { REG_CR_DAC, 0x90 },
+ { REG_CR_ADC, 0x90 },
+ { REG_CR_VIC, 0x03 },
+ { REG_IMR, 0xff },
+ { REG_IMR2, 0xff },
+ { REG_GCR_HPL, 0x06 },
+ { REG_GCR_HPR, 0x06 },
+ { REG_GCR_LIBYL, 0x06 },
+ { REG_GCR_LIBYR, 0x06 },
+};
+
+struct jz4780_codec {
+ void __iomem *base;
+ struct device *dev;
+ struct clk *clk;
+ struct regmap *regmap;
+};
+
+static int jz4780_codec_io_wait(struct jz4780_codec *codec)
+{
+ u32 reg;
+
+ return readl_poll_timeout(codec->base + REG_RGADW_OFF, reg,
+ !(reg & REG_RGADW_RGWR),
+ 1000, 1 * USEC_PER_SEC);
+}
+
+static int jz4780_codec_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct jz4780_codec *jzc = context;
+ int ret;
+
+ clk_enable(jzc->clk);
+
+ ret = jz4780_codec_io_wait(jzc);
+ if (ret)
+ goto out;
+
+ writel((reg & REG_RGADW_RGADDR_MASK) << REG_RGADW_RGADDR_SHIFT,
+ jzc->base + REG_RGADW_OFF);
+ *val = readl(jzc->base + REG_RGDATA_OFF) & REG_RGADW_RGDIN_MASK;
+
+out:
+ clk_disable(jzc->clk);
+ return ret;
+}
+
+static int jz4780_codec_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct jz4780_codec *jzc = context;
+ int ret;
+
+ clk_enable(jzc->clk);
+
+ ret = jz4780_codec_io_wait(jzc);
+ if (ret)
+ goto out;
+
+ writel(REG_RGADW_RGWR | ((reg & REG_RGADW_RGADDR_MASK)
+ << REG_RGADW_RGADDR_SHIFT)
+ | (val & REG_RGADW_RGDIN_MASK),
+ jzc->base + REG_RGADW_OFF);
+
+ ret = jz4780_codec_io_wait(jzc);
+
+out:
+ clk_disable(jzc->clk);
+ return ret;
+}
+
+static const char * const mic1_input_mux_text[] = {
+ "AIP1", "AIP2",
+};
+
+static const struct soc_enum mic1_input_enum =
+ SOC_ENUM_SINGLE(REG_CR_MIC1, 0, 2, mic1_input_mux_text);
+
+static const struct snd_kcontrol_new mic1_input_mux =
+ SOC_DAPM_ENUM("ADC Capture Route", mic1_input_enum);
+
+static const struct snd_kcontrol_new jz4780_codec_controls[] = {
+ SOC_DOUBLE_R("Master Capture Volume", REG_GCR_ADCL, REG_GCR_ADCR,
+ 0, 63, 0),
+
+ SOC_SINGLE("Mic Capture Volume", REG_GCR_MIC1, 0, 7, 0),
+ SOC_SINGLE("Mic Capture Switch", REG_CR_ADC, 7, 1, 1),
+
+ SOC_DOUBLE_R("Master Playback Volume", REG_GCR_DACL, REG_GCR_DACR,
+ 0, 31, 1),
+ SOC_SINGLE("Master Playback Switch", REG_CR_DAC, 7, 1, 1),
+
+ SOC_DOUBLE_R("Headphone Playback Volume", REG_GCR_HPL, REG_GCR_HPR,
+ 0, 31, 1),
+ SOC_SINGLE("Headphone Playback Switch", REG_CR_HP, 7, 1, 1),
+};
+
+static const struct snd_soc_dapm_widget jz4780_codec_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("ADC_SUPPLY", REG_CR_ADC, 4, 1, NULL, 0),
+ SND_SOC_DAPM_ADC("ADC", "Capture", REG_AICR_ADC, 4, 1),
+
+ SND_SOC_DAPM_SUPPLY("DAC_SUPPLY", REG_CR_DAC, 4, 1, NULL, 0),
+ SND_SOC_DAPM_DAC("DAC", "Playback", REG_AICR_DAC, 4, 1),
+
+ SND_SOC_DAPM_MUX("ADC Capture Route", REG_CR_MIC1, 4, 1, &mic1_input_mux),
+
+ SND_SOC_DAPM_MIXER("Headphones", REG_CR_HP, 4, 1, NULL, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Bias", REG_CR_MIC1, 5, 1),
+
+ SND_SOC_DAPM_INPUT("AIP1"),
+ SND_SOC_DAPM_INPUT("AIP2"),
+ SND_SOC_DAPM_INPUT("AIP3"),
+
+ SND_SOC_DAPM_OUTPUT("SYSCLK"),
+
+ SND_SOC_DAPM_OUTPUT("AOHPL"),
+ SND_SOC_DAPM_OUTPUT("AOHPR"),
+};
+
+static const struct snd_soc_dapm_route jz4780_codec_dapm_routes[] = {
+ {"ADC", NULL, "ADC_SUPPLY"},
+ {"ADC", NULL, "ADC Capture Route"},
+
+ {"ADC Capture Route", "AIP1", "AIP1"},
+ {"ADC Capture Route", "AIP2", "AIP2"},
+
+ {"DAC", NULL, "DAC_SUPPLY"},
+ {"Headphones", NULL, "DAC"},
+ {"AOHPL", NULL, "Headphones"},
+ {"AOHPR", NULL, "Headphones"},
+
+ /* SYSCLK output from the codec to the AIC is required to keep the
+ * DMA transfer going during playback when all audible outputs have
+ * been disabled.
+ */
+ {"SYSCLK", NULL, "DAC" },
+};
+
+static int jz4780_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ uint32_t val;
+ struct jz4780_codec *jzc = snd_soc_component_get_drvdata(dai->component);
+ struct regmap *regmap = jzc->regmap;
+
+ switch (params_rate(params)) {
+ case 8000:
+ val = 0;
+ break;
+ case 11025:
+ val = 1;
+ break;
+ case 12000:
+ val = 2;
+ break;
+ case 16000:
+ val = 3;
+ break;
+ case 22050:
+ val = 4;
+ break;
+ case 24000:
+ val = 5;
+ break;
+ case 32000:
+ val = 6;
+ break;
+ case 44100:
+ val = 7;
+ break;
+ case 48000:
+ val = 8;
+ break;
+ case 88200:
+ val = 9;
+ break;
+ case 96000:
+ val = 10;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+/* REMOVEME if works
+int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int mask, unsigned int value);
+=>
+int regmap_update_bits(struct regmap *map, unsigned int reg,
+ unsigned int mask, unsigned int val);
+*/
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(regmap, REG_FCR_DAC, REG_FCR_DAC_MASK, val);
+ else
+ regmap_update_bits(regmap, REG_FCR_ADC, REG_FCR_DAC_MASK, val);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16:
+ val = 0;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ val = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24:
+ val = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(regmap, REG_AICR_DAC,
+ REG_AICR_DAC_DAC_ADWL_MASK,
+ val << REG_AICR_DAC_DAC_ADWL_SHIFT);
+ } else {
+ regmap_update_bits(regmap, REG_AICR_ADC,
+ REG_AICR_DAC_DAC_ADWL_MASK,
+ val << REG_AICR_DAC_DAC_ADWL_SHIFT);
+
+ regmap_update_bits(regmap, REG_CR_MIC1,
+ REG_CR_MIC1_MICSTEREO_MASK,
+ REG_CR_MIC1_MICSTEREO_MONO);
+ regmap_update_bits(regmap, REG_CR_ADC,
+ REG_CR_ADC_ADC_LEFT_ONLY_MASK,
+ REG_CR_ADC_ADC_LEFT_ONLY_RCH_INACTIVE);
+ }
+
+ return 0;
+}
+
+static int jz4780_codec_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct jz4780_codec *jzc = snd_soc_component_get_drvdata(codec);
+ struct regmap *regmap = jzc->regmap;
+
+ regmap_update_bits(regmap, REG_CR_DAC, REG_CR_DAC_DAC_MUTE_MASK,
+ (!!mute) << REG_CR_DAC_DAC_MUTE_SHIFT);
+ return 0;
+}
+
+static struct snd_soc_dai_ops jz4780_codec_dai_ops = {
+ .hw_params = jz4780_codec_hw_params,
+// .digital_mute = jz4780_codec_mute,
+};
+
+static struct snd_soc_dai_driver jz4780_codec_dai = {
+ .name = "jz4780-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S18_3LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S18_3LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &jz4780_codec_dai_ops,
+};
+
+static int jz4780_codec_set_bias_level(struct snd_soc_component *codec,
+ enum snd_soc_bias_level level)
+{
+ struct jz4780_codec *jzc = snd_soc_component_get_drvdata(codec);
+ struct regmap *regmap = jzc->regmap;
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ regmap_update_bits(regmap, REG_CR_VIC, REG_CR_VIC_SB_SLEEP_MASK,
+ REG_CR_VIC_SB_SLEEP_NORMAL_MODE);
+ /* From manual, max time to power up after sleep */
+ msleep(900);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ regmap_update_bits(regmap, REG_CR_VIC, REG_CR_VIC_SB_MASK,
+ REG_CR_VIC_SB_NORMAL_MODE);
+ regmap_update_bits(regmap, REG_CR_VIC, REG_CR_VIC_SB_SLEEP_MASK,
+ REG_CR_VIC_SB_SLEEP_NORMAL_MODE);
+ /* From manual, max time to power up after sleep */
+ msleep(900);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(regmap, REG_CR_VIC, REG_CR_VIC_SB_SLEEP_MASK,
+ REG_CR_VIC_SB_SLEEP_SLEEP_MODE);
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(regmap, REG_CR_VIC, REG_CR_VIC_SB_MASK,
+ REG_CR_VIC_SB_POWERDOWN_MODE);
+ break;
+ default:
+ break;
+ }
+
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static int jz4780_codec_dev_probe(struct snd_soc_component *codec)
+{
+ struct jz4780_codec *jzc = snd_soc_component_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec);
+ struct regmap *regmap = jzc->regmap;
+
+ /* reset */
+ writel(REG_RGADW_ICRST, jzc->base);
+ udelay(2);
+ writel(0, jzc->base);
+
+ /*
+ * avoid
+ * [ 6.978427] codec 100200a4.audio-codec can not start from non-off bias with idle_bias_off==1
+ */
+ dapm->idle_bias_off = 0;
+
+ jz4780_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ /* select I2S */
+
+ regmap_update_bits(regmap, REG_AICR_DAC, REG_AICR_DAC_AUDIOIF_MASK,
+ REG_AICR_DAC_AUDIOIF_I2S);
+ regmap_update_bits(regmap, REG_AICR_ADC, REG_AICR_ADC_AUDIOIF_MASK,
+ REG_AICR_ADC_AUDIOIF_I2S);
+
+ /* select AIP2 input */
+ regmap_update_bits(regmap, REG_CR_MIC1, REG_CR_MIC1_MIC1_SEL_MASK,
+ REG_CR_MIC1_MIC1_SEL_AIP2);
+ regmap_update_bits(regmap, REG_CR_ADC, REG_CR_ADC_ADC_IN_SEL_MASK,
+ REG_CR_ADC_ADC_IN_SEL_AIN1);
+
+ /* disable mixer */
+ regmap_update_bits(regmap, REG_CR_MIX, REG_CR_MIX_MIX_EN_MASK,
+ REG_CR_MIX_MIX_EN_OFF);
+
+ return 0;
+}
+
+static int jz4780_codec_dev_remove(struct snd_soc_component *codec)
+{
+ jz4780_codec_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int jz4780_codec_suspend(struct snd_soc_component *codec, pm_message_t state)
+{
+ return jz4780_codec_set_bias_level(codec, SND_SOC_BIAS_OFF);
+}
+
+static int jz4780_codec_resume(struct snd_soc_component *codec)
+{
+ return jz4780_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+}
+
+#else
+#define jz4780_codec_suspend NULL
+#define jz4780_codec_resume NULL
+#endif
+
+static struct snd_soc_component_driver soc_codec_dev_jz4780_codec = {
+ .probe = jz4780_codec_dev_probe,
+/* FIXME
+ .remove = jz4780_codec_dev_remove,
+ .suspend = jz4780_codec_suspend,
+*/
+ .resume = jz4780_codec_resume,
+ .set_bias_level = jz4780_codec_set_bias_level,
+/* FIXME
+ .reg_cache_default = jz4780_codec_reg_defaults,
+ .reg_word_size = sizeof(uint8_t),
+ .reg_cache_size = 0x40,
+*/
+ .controls = jz4780_codec_controls,
+ .num_controls = ARRAY_SIZE(jz4780_codec_controls),
+ .dapm_widgets = jz4780_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(jz4780_codec_dapm_widgets),
+ .dapm_routes = jz4780_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(jz4780_codec_dapm_routes),
+};
+
+static const struct of_device_id jz4780_of_matches[] = {
+ { .compatible = "ingenic,jz4780-codec", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, jz4780_of_matches);
+
+static struct regmap_config jz4780_codec_regmap_config = {
+ .reg_bits = 7,
+ .val_bits = 8,
+
+ .reg_read = jz4780_codec_reg_read,
+ .reg_write = jz4780_codec_reg_write,
+
+ .max_register = REG_DR_ADC_AGC,
+/* FIXME
+ .volatile_reg = jz4780_codec_volatile,
+ .readable_reg = jz4780_codec_readable,
+ .writeable_reg = jz4780_codec_writeable,
+*/
+ .reg_defaults = jz4780_codec_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(jz4780_codec_reg_defaults),
+/* FIXME: REGCACHE_FLAT needs volatile/readable defined */
+ .cache_type = REGCACHE_NONE,
+};
+
+static int jz4780_codec_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int ret;
+ struct jz4780_codec *codec;
+
+ codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL);
+ if (!codec)
+ return -ENOMEM;
+
+ codec->dev = dev;
+
+ codec->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(codec->base))
+ return -PTR_ERR(codec->base);
+
+ codec->clk = devm_clk_get(&pdev->dev, "i2s");
+ if (IS_ERR(codec->clk))
+ return PTR_ERR(codec->clk);
+
+ clk_prepare(codec->clk);
+
+ codec->regmap = devm_regmap_init(dev, NULL, codec,
+ &jz4780_codec_regmap_config);
+ if (IS_ERR(codec->regmap))
+ return PTR_ERR(codec->regmap);
+
+ platform_set_drvdata(pdev, codec);
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_codec_dev_jz4780_codec, &jz4780_codec_dai, 1);
+ if (ret)
+ goto out_err_register;
+
+ return 0;
+
+out_err_register:
+ clk_unprepare(codec->clk);
+
+ return ret;
+}
+
+static int jz4780_codec_remove(struct platform_device *pdev)
+{
+ struct jz4780_codec *codec = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_component(&pdev->dev);
+
+ clk_unprepare(codec->clk);
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(codec);
+
+ return 0;
+}
+
+static struct platform_driver jz4780_codec_driver = {
+ .probe = jz4780_codec_probe,
+ .remove = jz4780_codec_remove,
+ .driver = {
+ .name = "jz4780-codec",
+ .of_match_table = jz4780_of_matches
+ },
+};
+
+module_platform_driver(jz4780_codec_driver);
+
+MODULE_AUTHOR("Paul Burton <paul....@imgtec.com>");
+MODULE_DESCRIPTION("Ingenic JZ4780 ASoC codec driver");

H. Nikolaus Schaller

unread,
May 3, 2021, 2:20:15 AM5/3/21
to pa...@boddie.org.uk, mips-creat...@googlegroups.com, letux-...@openphoenux.org, maa...@treewalker.org
CI20 and Alpha400 code is now also in git: https://git.goldelico.com/?p=letux-kernel.git;a=shortlog;h=refs/heads/letux-5.12.y

> Am 02.05.2021 um 11:16 schrieb H. Nikolaus Schaller <h...@goldelico.com>:
>
> Updates v2:
> * replace jz4780 codec based on jz4770 by code specific for the jz4780 from the 3.18 kernel
> * add ci20-audio from the 3.18 kernel to replace simple-audio-card because the CI20 needs some special setup
> for mic type detecion and HDMI audio
> * enable kernel module configs
> * debugged to create an audio card (which does not yet fully work)
>
> Notes:
> - not cleaned
> - not checkpatched
> - no YAML
> - needs CI20 HDMI solution as basis (which is not upstream)
Reply all
Reply to author
Forward
0 new messages