From 64fcc1fd323835a9185baafa50d2087603c4051c Mon Sep 17 00:00:00 2001 From: Pascal Huerst Date: Mon, 20 Apr 2015 11:12:03 +0200 Subject: ASoC: adau1701: add regulator consumer support The adau1701 has two power domains, DVDD and AVDD. Enable them both as long as the codec is in use. Signed-off-by: Pascal Huerst Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/adi,adau1701.txt | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/adi,adau1701.txt b/Documentation/devicetree/bindings/sound/adi,adau1701.txt index 547a49b56a62..0d1128ce2ea7 100644 --- a/Documentation/devicetree/bindings/sound/adi,adau1701.txt +++ b/Documentation/devicetree/bindings/sound/adi,adau1701.txt @@ -20,6 +20,8 @@ Optional properties: pin configurations as described in the datasheet, table 53. Note that the value of this property has to be prefixed with '/bits/ 8'. + - avdd-supply: Power supply for AVDD, providing 3.3V + - dvdd-supply: Power supply for DVDD, providing 3.3V Examples: @@ -28,6 +30,8 @@ Examples: compatible = "adi,adau1701"; reg = <0x34>; reset-gpio = <&gpio 23 0>; + avdd-supply = <&vdd_3v3_reg>; + dvdd-supply = <&vdd_3v3_reg>; adi,pll-mode-gpios = <&gpio 24 0 &gpio 25 0>; adi,pin-config = /bits/ 8 <0x4 0x7 0x5 0x5 0x4 0x4 0x4 0x4 0x4 0x4 0x4 0x4>; -- cgit v1.2.3 From 12e180a27f3e066a4ed4a446d428fd117f168beb Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Mon, 27 Apr 2015 21:21:02 +0800 Subject: ALSA: Docs: Add documentation for Jack kcontrols Add documentation describing Jack kcontrols and how to use them with HD-Audio and ASoC. Signed-off-by: Jie Yang Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/Jack-Controls.txt | 43 ++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 Documentation/sound/alsa/Jack-Controls.txt (limited to 'Documentation') diff --git a/Documentation/sound/alsa/Jack-Controls.txt b/Documentation/sound/alsa/Jack-Controls.txt new file mode 100644 index 000000000000..fe1c5e0c8555 --- /dev/null +++ b/Documentation/sound/alsa/Jack-Controls.txt @@ -0,0 +1,43 @@ +Why we need Jack kcontrols +========================== + +ALSA uses kcontrols to export audio controls(switch, volume, Mux, ...) +to user space. This means userspace applications like pulseaudio can +switch off headphones and switch on speakers when no headphones are +pluged in. + +The old ALSA jack code only created input devices for each registered +jack. These jack input devices are not readable by userspace devices +that run as non root. + +The new jack code creates embedded jack kcontrols for each jack that +can be read by any process. + +This can be combined with UCM to allow userspace to route audio more +intelligently based on jack insertion or removal events. + +Jack Kcontrol Internals +======================= + +Each jack will have a kcontrol list, so that we can create a kcontrol +and attach it to the jack, at jack creation stage. We can also add a +kcontrol to an existing jack, at anytime when required. + +Those kcontrols will be freed automatically when the Jack is freed. + +How to use jack kcontrols +========================= + +In order to keep compatibility, snd_jack_new() has been modified by +adding two params :- + + - @initial_kctl: if true, create a kcontrol and add it to the jack + list. + - @phantom_jack: Don't create a input device for phantom jacks. + +HDA jacks can set phantom_jack to true in order to create a phantom +jack and set initial_kctl to true to create an initial kcontrol with +the correct id. + +ASoC jacks should set initial_kctl as false. The pin name will be +assigned as the jack kcontrol name. -- cgit v1.2.3 From ee5d4df7298336a4c40140a1ce179e11ed179b03 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Sun, 3 May 2015 17:00:17 -0700 Subject: ASoC: tas571x: Add DT binding document Document the bindings for the soon-to-be-added tas571x driver. Signed-off-by: Kevin Cernekee Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/tas571x.txt | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/tas571x.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/tas571x.txt b/Documentation/devicetree/bindings/sound/tas571x.txt new file mode 100644 index 000000000000..0ac31d8d5ac4 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tas571x.txt @@ -0,0 +1,41 @@ +Texas Instruments TAS5711/TAS5717/TAS5719 stereo power amplifiers + +The codec is controlled through an I2C interface. It also has two other +signals that can be wired up to GPIOs: reset (strongly recommended), and +powerdown (optional). + +Required properties: + +- compatible: "ti,tas5711", "ti,tas5717", or "ti,tas5719" +- reg: The I2C address of the device +- #sound-dai-cells: must be equal to 0 + +Optional properties: + +- reset-gpios: GPIO specifier for the TAS571x's active low reset line +- pdn-gpios: GPIO specifier for the TAS571x's active low powerdown line +- clocks: clock phandle for the MCLK input +- clock-names: should be "mclk" +- AVDD-supply: regulator phandle for the AVDD supply (all chips) +- DVDD-supply: regulator phandle for the DVDD supply (all chips) +- HPVDD-supply: regulator phandle for the HPVDD supply (5717/5719) +- PVDD_AB-supply: regulator phandle for the PVDD_AB supply (5717/5719) +- PVDD_CD-supply: regulator phandle for the PVDD_CD supply (5717/5719) +- PVDD_A-supply: regulator phandle for the PVDD_A supply (5711) +- PVDD_B-supply: regulator phandle for the PVDD_B supply (5711) +- PVDD_C-supply: regulator phandle for the PVDD_C supply (5711) +- PVDD_D-supply: regulator phandle for the PVDD_D supply (5711) + +Example: + + tas5717: audio-codec@2a { + compatible = "ti,tas5717"; + reg = <0x2a>; + #sound-dai-cells = <0>; + + reset-gpios = <&gpio5 1 GPIO_ACTIVE_LOW>; + pdn-gpios = <&gpio5 2 GPIO_ACTIVE_LOW>; + + clocks = <&clk_core CLK_I2S>; + clock-names = "mclk"; + }; -- cgit v1.2.3 From 596f74ec275b0ec608e9450c937c6a29ba91b352 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 27 Apr 2015 14:49:09 +0200 Subject: ASoC: rsnd: Use generic names for device nodes rcar_sound -> sound Signed-off-by: Geert Uytterhoeven Acked-by: Simon Horman Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/renesas,rsnd.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt index f316ce1f214a..62ece4c59da7 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt @@ -47,7 +47,7 @@ DAI subnode properties: Example: -rcar_sound: rcar_sound@ec500000 { +rcar_sound: sound@ec500000 { #sound-dai-cells = <1>; compatible = "renesas,rcar_sound-r8a7791", "renesas,rcar_sound-gen2"; reg = <0 0xec500000 0 0x1000>, /* SCU */ -- cgit v1.2.3 From 40579e0b88580cb8fd53218635ab0afbdb3a4919 Mon Sep 17 00:00:00 2001 From: Marek Belisko Date: Thu, 7 May 2015 21:29:31 +0200 Subject: ASoC: gtm601: Document GTM601 bindings Add small documentation for GTM601 UMTS modem audio interface. Signed-off-by: Marek Belisko Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/gtm601.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/gtm601.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/gtm601.txt b/Documentation/devicetree/bindings/sound/gtm601.txt new file mode 100644 index 000000000000..5efc8c068de0 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/gtm601.txt @@ -0,0 +1,13 @@ +GTM601 UMTS modem audio interface CODEC + +This device has no configuration interface. Sample rate is fixed - 8kHz. + +Required properties: + + - compatible : "option,gtm601" + +Example: + +codec: gtm601_codec { + compatible = "option,gtm601"; +}; -- cgit v1.2.3 From c778b4726a13ed38f8d36c926b7b0d5144c562de Mon Sep 17 00:00:00 2001 From: Marek Belisko Date: Fri, 8 May 2015 21:02:34 +0200 Subject: ASoC: bt-sco: Add devicetree support for bt-sco codec Add devicetree support for bluetooth SCO link codec. Signed-off-by: Marek Belisko Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/bt-sco.txt | 13 +++++++++++++ Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + sound/soc/codecs/bt-sco.c | 9 +++++++++ 3 files changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/bt-sco.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/bt-sco.txt b/Documentation/devicetree/bindings/sound/bt-sco.txt new file mode 100644 index 000000000000..29b8e5d40203 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/bt-sco.txt @@ -0,0 +1,13 @@ +Bluetooth-SCO audio CODEC + +This device support generic Bluetooth SCO link. + +Required properties: + + - compatible : "delta,dfbmcs320" + +Example: + +codec: bt_sco { + compatible = "delta,dfbmcs320"; +}; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 80339192c93e..b6969e477bf3 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -54,6 +54,7 @@ cosmic Cosmic Circuits crystalfontz Crystalfontz America, Inc. dallas Maxim Integrated Products (formerly Dallas Semiconductor) davicom DAVICOM Semiconductor, Inc. +delta Delta Electronics, Inc. denx Denx Software Engineering digi Digi International Inc. digilent Diglent, Inc. diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c index 9d0b794d3005..b084ad113e96 100644 --- a/sound/soc/codecs/bt-sco.c +++ b/sound/soc/codecs/bt-sco.c @@ -74,9 +74,18 @@ static const struct platform_device_id bt_sco_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids); +#if defined(CONFIG_OF) +static const struct of_device_id bt_sco_codec_of_match[] = { + { .compatible = "delta,dfbmcs320", }, + {}, +}; +MODULE_DEVICE_TABLE(of, bt_sco_codec_of_match); +#endif + static struct platform_driver bt_sco_driver = { .driver = { .name = "bt-sco", + .of_match_table = of_match_ptr(bt_sco_codec_of_match), }, .probe = bt_sco_probe, .remove = bt_sco_remove, -- cgit v1.2.3 From c354b54cfdf63587154da4fa0731c1fbda44c589 Mon Sep 17 00:00:00 2001 From: Sergej Sawazki Date: Wed, 13 May 2015 11:39:01 +0200 Subject: ASoC: wm8741: Add differential mono mode support The WM8741 DAC supports several differential output modes (stereo, stereo reversed, mono left, mono right). Add platform data and DT bindings to configure it. Signed-off-by: Sergej Sawazki Acked-by: Charles Keepax Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/wm8741.txt | 11 ++ sound/soc/codecs/wm8741.c | 129 ++++++++++++++++++--- sound/soc/codecs/wm8741.h | 10 ++ 3 files changed, 137 insertions(+), 13 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/wm8741.txt b/Documentation/devicetree/bindings/sound/wm8741.txt index 74bda58c1bcf..a13315408719 100644 --- a/Documentation/devicetree/bindings/sound/wm8741.txt +++ b/Documentation/devicetree/bindings/sound/wm8741.txt @@ -10,9 +10,20 @@ Required properties: - reg : the I2C address of the device for I2C, the chip select number for SPI. +Optional properties: + + - diff-mode: Differential output mode configuration. Default value for field + DIFF in register R8 (MODE_CONTROL_2). If absent, the default is 0, shall be: + 0 = stereo + 1 = mono left + 2 = stereo reversed + 3 = mono right + Example: codec: wm8741@1a { compatible = "wlf,wm8741"; reg = <0x1a>; + + diff-mode = <3>; }; diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 9e71c768966f..c065ea166875 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -41,6 +41,7 @@ static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = { /* codec private data */ struct wm8741_priv { + struct wm8741_platform_data pdata; struct regmap *regmap; struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES]; unsigned int sysclk; @@ -87,13 +88,27 @@ static int wm8741_reset(struct snd_soc_codec *codec) static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0); static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0); -static const struct snd_kcontrol_new wm8741_snd_controls[] = { +static const struct snd_kcontrol_new wm8741_snd_controls_stereo[] = { SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION, WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine), SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION, WM8741_DACRMSB_ATTENUATION, 0, 511, 1, dac_tlv), }; +static const struct snd_kcontrol_new wm8741_snd_controls_mono_left[] = { +SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION, + 1, 255, 1, dac_tlv_fine), +SOC_SINGLE_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION, + 0, 511, 1, dac_tlv), +}; + +static const struct snd_kcontrol_new wm8741_snd_controls_mono_right[] = { +SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACRLSB_ATTENUATION, + 1, 255, 1, dac_tlv_fine), +SOC_SINGLE_TLV("Playback Volume", WM8741_DACRMSB_ATTENUATION, + 0, 511, 1, dac_tlv), +}; + static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = { SND_SOC_DAPM_DAC("DACL", "Playback", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_DAC("DACR", "Playback", SND_SOC_NOPM, 0, 0), @@ -398,7 +413,7 @@ static struct snd_soc_dai_driver wm8741_dai = { .name = "wm8741", .playback = { .stream_name = "Playback", - .channels_min = 2, /* Mono modes not yet supported */ + .channels_min = 2, .channels_max = 2, .rates = WM8741_RATES, .formats = WM8741_FORMATS, @@ -416,6 +431,65 @@ static int wm8741_resume(struct snd_soc_codec *codec) #define wm8741_resume NULL #endif +static int wm8741_configure(struct snd_soc_codec *codec) +{ + struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); + + /* Configure differential mode */ + switch (wm8741->pdata.diff_mode) { + case WM8741_DIFF_MODE_STEREO: + case WM8741_DIFF_MODE_STEREO_REVERSED: + case WM8741_DIFF_MODE_MONO_LEFT: + case WM8741_DIFF_MODE_MONO_RIGHT: + snd_soc_update_bits(codec, WM8741_MODE_CONTROL_2, + WM8741_DIFF_MASK, + wm8741->pdata.diff_mode << WM8741_DIFF_SHIFT); + break; + default: + return -EINVAL; + } + + /* Change some default settings - latch VU */ + snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION, + WM8741_UPDATELL, WM8741_UPDATELL); + snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION, + WM8741_UPDATELM, WM8741_UPDATELM); + snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION, + WM8741_UPDATERL, WM8741_UPDATERL); + snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION, + WM8741_UPDATERM, WM8741_UPDATERM); + + return 0; +} + +static int wm8741_add_controls(struct snd_soc_codec *codec) +{ + struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); + + switch (wm8741->pdata.diff_mode) { + case WM8741_DIFF_MODE_STEREO: + case WM8741_DIFF_MODE_STEREO_REVERSED: + snd_soc_add_codec_controls(codec, + wm8741_snd_controls_stereo, + ARRAY_SIZE(wm8741_snd_controls_stereo)); + break; + case WM8741_DIFF_MODE_MONO_LEFT: + snd_soc_add_codec_controls(codec, + wm8741_snd_controls_mono_left, + ARRAY_SIZE(wm8741_snd_controls_mono_left)); + break; + case WM8741_DIFF_MODE_MONO_RIGHT: + snd_soc_add_codec_controls(codec, + wm8741_snd_controls_mono_right, + ARRAY_SIZE(wm8741_snd_controls_mono_right)); + break; + default: + return -EINVAL; + } + + return 0; +} + static int wm8741_probe(struct snd_soc_codec *codec) { struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); @@ -434,15 +508,17 @@ static int wm8741_probe(struct snd_soc_codec *codec) goto err_enable; } - /* Change some default settings - latch VU */ - snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION, - WM8741_UPDATELL, WM8741_UPDATELL); - snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION, - WM8741_UPDATELM, WM8741_UPDATELM); - snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION, - WM8741_UPDATERL, WM8741_UPDATERL); - snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION, - WM8741_UPDATERM, WM8741_UPDATERM); + ret = wm8741_configure(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to change default settings\n"); + goto err_enable; + } + + ret = wm8741_add_controls(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to add controls\n"); + goto err_enable; + } dev_dbg(codec->dev, "Successful registration\n"); return ret; @@ -467,8 +543,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8741 = { .remove = wm8741_remove, .resume = wm8741_resume, - .controls = wm8741_snd_controls, - .num_controls = ARRAY_SIZE(wm8741_snd_controls), .dapm_widgets = wm8741_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets), .dapm_routes = wm8741_dapm_routes, @@ -493,6 +567,23 @@ static const struct regmap_config wm8741_regmap = { .readable_reg = wm8741_readable, }; +static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741) +{ + const struct wm8741_platform_data *pdata = dev_get_platdata(dev); + u32 diff_mode; + + if (dev->of_node) { + if (of_property_read_u32(dev->of_node, "diff-mode", &diff_mode) + >= 0) + wm8741->pdata.diff_mode = diff_mode; + } else { + if (pdata != NULL) + memcpy(&wm8741->pdata, pdata, sizeof(wm8741->pdata)); + } + + return 0; +} + #if IS_ENABLED(CONFIG_I2C) static int wm8741_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) @@ -522,6 +613,12 @@ static int wm8741_i2c_probe(struct i2c_client *i2c, return ret; } + wm8741_set_pdata(&i2c->dev, wm8741); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to set pdata: %d\n", ret); + return ret; + } + i2c_set_clientdata(i2c, wm8741); ret = snd_soc_register_codec(&i2c->dev, @@ -582,6 +679,12 @@ static int wm8741_spi_probe(struct spi_device *spi) return ret; } + wm8741_set_pdata(&spi->dev, wm8741); + if (ret != 0) { + dev_err(&spi->dev, "Failed to set pdata: %d\n", ret); + return ret; + } + spi_set_drvdata(spi, wm8741); ret = snd_soc_register_codec(&spi->dev, diff --git a/sound/soc/codecs/wm8741.h b/sound/soc/codecs/wm8741.h index 56c1b1d4a681..c8835f65f342 100644 --- a/sound/soc/codecs/wm8741.h +++ b/sound/soc/codecs/wm8741.h @@ -194,6 +194,12 @@ #define WM8741_DITHER_SHIFT 0 /* DITHER - [1:0] */ #define WM8741_DITHER_WIDTH 2 /* DITHER - [1:0] */ +/* DIFF field values */ +#define WM8741_DIFF_MODE_STEREO 0 /* stereo normal */ +#define WM8741_DIFF_MODE_STEREO_REVERSED 2 /* stereo reversed */ +#define WM8741_DIFF_MODE_MONO_LEFT 1 /* mono left */ +#define WM8741_DIFF_MODE_MONO_RIGHT 3 /* mono right */ + /* * R32 (0x20) - ADDITONAL_CONTROL_1 */ @@ -208,4 +214,8 @@ #define WM8741_SYSCLK 0 +struct wm8741_platform_data { + u32 diff_mode; /* Differential Output Mode */ +}; + #endif -- cgit v1.2.3 From b3b10e99b73b5e079fdb9bdaa1dad43b53e330cd Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Wed, 13 May 2015 08:25:15 -0700 Subject: ASoC: rt5677: Add reset-gpio dts option It allows to configure codec's RESET pin gpio Signed-off-by: Anatol Pomozov Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/rt5677.txt | 2 ++ sound/soc/codecs/rt5677.c | 32 ++++++++++++++++++++-- sound/soc/codecs/rt5677.h | 1 + 3 files changed, 33 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/rt5677.txt b/Documentation/devicetree/bindings/sound/rt5677.txt index 740ff771aa8b..f07078997f87 100644 --- a/Documentation/devicetree/bindings/sound/rt5677.txt +++ b/Documentation/devicetree/bindings/sound/rt5677.txt @@ -18,6 +18,7 @@ Required properties: Optional properties: - realtek,pow-ldo2-gpio : The GPIO that controls the CODEC's POW_LDO2 pin. +- realtek,reset-gpio : The GPIO that controls the CODEC's RESET pin. - realtek,in1-differential - realtek,in2-differential @@ -70,6 +71,7 @@ rt5677 { realtek,pow-ldo2-gpio = <&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>; + realtek,reset-gpio = <&gpio TEGRA_GPIO(BB, 3) GPIO_ACTIVE_LOW>; realtek,in1-differential = "true"; realtek,gpio-config = /bits/ 8 <0 0 0 0 0 2>; /* pull up GPIO6 */ realtek,jd2-gpio = <3>; /* Enables Jack detection for GPIO6 */ diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index c73105e75c1a..aba00fd8dfc4 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -4763,6 +4763,8 @@ static int rt5677_remove(struct snd_soc_codec *codec) regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec); if (gpio_is_valid(rt5677->pow_ldo2)) gpio_set_value_cansleep(rt5677->pow_ldo2, 0); + if (gpio_is_valid(rt5677->reset_pin)) + gpio_set_value_cansleep(rt5677->reset_pin, 0); return 0; } @@ -4778,6 +4780,8 @@ static int rt5677_suspend(struct snd_soc_codec *codec) if (gpio_is_valid(rt5677->pow_ldo2)) gpio_set_value_cansleep(rt5677->pow_ldo2, 0); + if (gpio_is_valid(rt5677->reset_pin)) + gpio_set_value_cansleep(rt5677->reset_pin, 0); } return 0; @@ -4788,10 +4792,13 @@ static int rt5677_resume(struct snd_soc_codec *codec) struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); if (!rt5677->dsp_vad_en) { - if (gpio_is_valid(rt5677->pow_ldo2)) { + if (gpio_is_valid(rt5677->pow_ldo2)) gpio_set_value_cansleep(rt5677->pow_ldo2, 1); + if (gpio_is_valid(rt5677->reset_pin)) + gpio_set_value_cansleep(rt5677->reset_pin, 1); + if (gpio_is_valid(rt5677->pow_ldo2) || + gpio_is_valid(rt5677->reset_pin)) msleep(10); - } regcache_cache_only(rt5677->regmap, false); regcache_sync(rt5677->regmap); @@ -5029,6 +5036,8 @@ static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np) rt5677->pow_ldo2 = of_get_named_gpio(np, "realtek,pow-ldo2-gpio", 0); + rt5677->reset_pin = of_get_named_gpio(np, + "realtek,reset-gpio", 0); /* * POW_LDO2 is optional (it may be statically tied on the board). @@ -5039,6 +5048,9 @@ static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np) if (!gpio_is_valid(rt5677->pow_ldo2) && (rt5677->pow_ldo2 != -ENOENT)) return rt5677->pow_ldo2; + if (!gpio_is_valid(rt5677->reset_pin) && + (rt5677->reset_pin != -ENOENT)) + return rt5677->reset_pin; of_property_read_u8_array(np, "realtek,gpio-config", rt5677->pdata.gpio_config, RT5677_GPIO_NUM); @@ -5140,6 +5152,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, } } else { rt5677->pow_ldo2 = -EINVAL; + rt5677->reset_pin = -EINVAL; } if (gpio_is_valid(rt5677->pow_ldo2)) { @@ -5151,6 +5164,21 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, rt5677->pow_ldo2, ret); return ret; } + } + + if (gpio_is_valid(rt5677->reset_pin)) { + ret = devm_gpio_request_one(&i2c->dev, rt5677->reset_pin, + GPIOF_OUT_INIT_HIGH, + "RT5677 RESET"); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to request RESET %d: %d\n", + rt5677->reset_pin, ret); + return ret; + } + } + + if (gpio_is_valid(rt5677->pow_ldo2) || + gpio_is_valid(rt5677->reset_pin)) { /* Wait a while until I2C bus becomes available. The datasheet * does not specify the exact we should wait but startup * sequence mentiones at least a few milliseconds. diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index 62571d071a8d..7eca38a23255 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -1776,6 +1776,7 @@ struct rt5677_priv { int pll_in; int pll_out; int pow_ldo2; /* POW_LDO2 pin */ + int reset_pin; /* RESET pin */ enum rt5677_type type; #ifdef CONFIG_GPIOLIB struct gpio_chip gpio_chip; -- cgit v1.2.3 From b016951e897b5bd06abfb732012f67b461fb1e2e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 19 May 2015 10:20:13 +0200 Subject: ALSA: hda - Add headset-mode* model options for ALC269 & co Modern machines tend to have only one headset jack nowadays, and they often need these quirks. Let's allow them applicable via model option for ease of debugging. Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/HD-Audio-Models.txt | 2 ++ sound/pci/hda/patch_realtek.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt index 5a3163cac6c3..d92b85d38adc 100644 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/Documentation/sound/alsa/HD-Audio-Models.txt @@ -29,6 +29,8 @@ ALC269/270/275/276/28x/29x alc271-dmic Enable ALC271X digital mic workaround inv-dmic Inverted internal mic workaround headset-mic Indicates a combined headset (headphone+mic) jack + headset-mode More comprehensive headset support for ALC269 & co + headset-mode-no-hp-mic Headset mode support without headphone mic lenovo-dock Enables docking station I/O for some Lenovos dell-headset-multi Headset jack, which can also be used as mic-in dell-headset-dock Headset jack (without mic-in), and also dock I/O diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6e1c8c34e841..4d11fbfca5e2 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5242,6 +5242,8 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC271_FIXUP_DMIC, .name = "alc271-dmic"}, {.id = ALC269_FIXUP_INV_DMIC, .name = "inv-dmic"}, {.id = ALC269_FIXUP_HEADSET_MIC, .name = "headset-mic"}, + {.id = ALC269_FIXUP_HEADSET_MODE, .name = "headset-mode"}, + {.id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, .name = "headset-mode-no-hp-mic"}, {.id = ALC269_FIXUP_LENOVO_DOCK, .name = "lenovo-dock"}, {.id = ALC269_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"}, {.id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "dell-headset-multi"}, -- cgit v1.2.3 From 1882b0577b5b67e7b663be4e8126997423b6cb6f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 19 May 2015 10:21:57 +0200 Subject: ALSA: hda - Sync HD-Audio-Models.txt for Realtek codecs Added missing model entries and updated the codec names. Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/HD-Audio-Models.txt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt index d92b85d38adc..ec099d4343f2 100644 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/Documentation/sound/alsa/HD-Audio-Models.txt @@ -11,7 +11,10 @@ ALC880 ALC260 ====== - N/A + gpio1 Enable GPIO1 + coef Enable EAPD via COEF table + fujitsu Quirk for FSC S7020 + fujitsu-jwse Quirk for FSC S7020 with jack modes and HP mic support ALC262 ====== @@ -20,8 +23,9 @@ ALC262 ALC267/268 ========== inv-dmic Inverted internal mic workaround + hp-eapd Disable HP EAPD on NID 0x15 -ALC269/270/275/276/28x/29x +ALC22x/23x/25x/269/27x/28x/29x (and vendor-specific ALC3xxx models) ====== laptop-amic Laptops with analog-mic input laptop-dmic Laptops with digital-mic input @@ -32,8 +36,12 @@ ALC269/270/275/276/28x/29x headset-mode More comprehensive headset support for ALC269 & co headset-mode-no-hp-mic Headset mode support without headphone mic lenovo-dock Enables docking station I/O for some Lenovos + hp-gpio-led GPIO LED support on HP laptops dell-headset-multi Headset jack, which can also be used as mic-in dell-headset-dock Headset jack (without mic-in), and also dock I/O + alc283-dac-wcaps Fixups for Chromebook with ALC283 + alc283-sense-combo Combo jack sensing on ALC283 + tpt440-dock Pin configs for Lenovo Thinkpad Dock support ALC66x/67x/892 ============== -- cgit v1.2.3 From b202836a548c1985137b5b648a4afe3cc5959f4b Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 21 May 2015 22:53:45 +0100 Subject: ASoC: qcom: Document apq8016 bindings. This patch updates lpass bindings with apq8016 specific bindings. Tested-by: Kenneth Westfield Acked-by: Kenneth Westfield Signed-off-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt index e00732dac939..21c648328be9 100644 --- a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt @@ -4,12 +4,21 @@ This node models the Qualcomm Technologies Low-Power Audio SubSystem (LPASS). Required properties: -- compatible : "qcom,lpass-cpu" +- compatible : "qcom,lpass-cpu" or "qcom,apq8016-lpass-cpu" - clocks : Must contain an entry for each entry in clock-names. - clock-names : A list which must include the following entries: * "ahbix-clk" * "mi2s-osr-clk" * "mi2s-bit-clk" + : required clocks for "qcom,lpass-cpu-apq8016" + * "ahbix-clk" + * "mi2s-bit-clk0" + * "mi2s-bit-clk1" + * "mi2s-bit-clk2" + * "mi2s-bit-clk3" + * "pcnoc-mport-clk" + * "pcnoc-sway-clk" + - interrupts : Must contain an entry for each entry in interrupt-names. - interrupt-names : A list which must include the following entries: @@ -22,6 +31,8 @@ Required properties: - reg-names : A list which must include the following entries: * "lpass-lpaif" + + Optional properties: - qcom,adsp : Phandle for the audio DSP node -- cgit v1.2.3 From 7667f716e502f2b3e42085738b205ddc9abcff25 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 1 Jun 2015 12:44:15 +0200 Subject: ASoC: rsnd: Document r8a7778-specific binding Add the missing r8a7778-specific compatible value, which is already in use since v4.1-rc1. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/renesas,rsnd.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt index f316ce1f214a..14f467345994 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt @@ -5,6 +5,7 @@ Required properties: "renesas,rcar_sound-gen1" if generation1, and "renesas,rcar_sound-gen2" if generation2 Examples with soctypes are: + - "renesas,rcar_sound-r8a7778" (R-Car M1A) - "renesas,rcar_sound-r8a7790" (R-Car H2) - "renesas,rcar_sound-r8a7791" (R-Car M2-W) - reg : Should contain the register physical address. -- cgit v1.2.3 From bb13f0e08d16a6a303aab786b2aaf2ca76747cfb Mon Sep 17 00:00:00 2001 From: "Fang, Yang A" Date: Fri, 29 May 2015 11:56:10 -0700 Subject: ASoC: max98090: read micbias from device property This patch reads max98090 micbias from acpi or dt Signed-off-by: Fang, Yang A Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/max98090.txt | 6 ++++++ sound/soc/codecs/max98090.c | 13 ++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/max98090.txt b/Documentation/devicetree/bindings/sound/max98090.txt index aa802a274520..4e3be6682c98 100644 --- a/Documentation/devicetree/bindings/sound/max98090.txt +++ b/Documentation/devicetree/bindings/sound/max98090.txt @@ -18,6 +18,12 @@ Optional properties: - maxim,dmic-freq: Frequency at which to clock DMIC +- maxim,micbias: Micbias voltage applies to the analog mic, valid voltages value are: + 0 - 2.2v + 1 - 2.55v + 2 - 2.4v + 3 - 2.8v + Pins on the device (for linking into audio routes): * MIC1 diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 3e33ef2acf3c..9d80c68abdd5 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2422,6 +2422,8 @@ static int max98090_probe(struct snd_soc_codec *codec) struct max98090_cdata *cdata; enum max98090_type devtype; int ret = 0; + int err; + unsigned int micbias; dev_dbg(codec->dev, "max98090_probe\n"); @@ -2506,8 +2508,17 @@ static int max98090_probe(struct snd_soc_codec *codec) snd_soc_write(codec, M98090_REG_BIAS_CONTROL, M98090_VCM_MODE_MASK); + err = device_property_read_u32(codec->dev, "maxim,micbias", &micbias); + if (err) { + micbias = M98090_MBVSEL_2V8; + dev_info(codec->dev, "use default 2.8v micbias\n"); + } else if (micbias < M98090_MBVSEL_2V2 || micbias > M98090_MBVSEL_2V8) { + dev_err(codec->dev, "micbias out of range 0x%x\n", micbias); + micbias = M98090_MBVSEL_2V8; + } + snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE, - M98090_MBVSEL_MASK, M98090_MBVSEL_2V8); + M98090_MBVSEL_MASK, micbias); max98090_add_widgets(codec); -- cgit v1.2.3 From 0637e965ba20da9cb29cade3a7026db307473f05 Mon Sep 17 00:00:00 2001 From: Jun Nie Date: Thu, 4 Jun 2015 11:41:20 +0800 Subject: dt: Add documentation for the ZTE SPDIF controller This patch adds the devicetree documentation for the ZTE zx296702 SPDIF audio controller. Signed-off-by: Jun Nie Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/zte,zx-spdif.txt | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/zte,zx-spdif.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/zte,zx-spdif.txt b/Documentation/devicetree/bindings/sound/zte,zx-spdif.txt new file mode 100644 index 000000000000..989544ea6eb5 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/zte,zx-spdif.txt @@ -0,0 +1,28 @@ +ZTE ZX296702 SPDIF controller + +Required properties: + - compatible : Must be "zte,zx296702-spdif" + - reg : Must contain SPDIF core's registers location and length + - clocks : Pairs of phandle and specifier referencing the controller's clocks. + - clock-names: "tx" for the clock to the SPDIF interface. + - dmas: Pairs of phandle and specifier for the DMA channel that is used by + the core. The core expects one dma channel for transmit. + - dma-names : Must be "tx" + +For more details on the 'dma', 'dma-names', 'clock' and 'clock-names' properties +please check: + * resource-names.txt + * clock/clock-bindings.txt + * dma/dma.txt + +Example: + spdif0: spdif0@0b004000 { + compatible = "zte,zx296702-spdif"; + reg = <0x0b004000 0x1000>; + clocks = <&lsp0clk ZX296702_SPDIF0_DIV>; + clock-names = "tx"; + interrupts = ; + dmas = <&dma 4>; + dma-names = "tx"; + status = "okay"; + }; -- cgit v1.2.3 From dc772a4cf76113d7269e4fb1c45e5d85c0cf458e Mon Sep 17 00:00:00 2001 From: Jun Nie Date: Thu, 4 Jun 2015 11:41:21 +0800 Subject: dt: Add documentation for the ZTE I2S controller This patch adds the devicetree documentation for the ZTE zx296702 I2S audio controller. Signed-off-by: Jun Nie Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/zte,zx-i2s.txt | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/zte,zx-i2s.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/zte,zx-i2s.txt b/Documentation/devicetree/bindings/sound/zte,zx-i2s.txt new file mode 100644 index 000000000000..7e5aa6f6b5a1 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/zte,zx-i2s.txt @@ -0,0 +1,44 @@ +ZTE ZX296702 I2S controller + +Required properties: + - compatible : Must be "zte,zx296702-i2s" + - reg : Must contain I2S core's registers location and length + - clocks : Pairs of phandle and specifier referencing the controller's clocks. + - clock-names: "tx" for the clock to the I2S interface. + - dmas: Pairs of phandle and specifier for the DMA channel that is used by + the core. The core expects two dma channels for transmit. + - dma-names : Must be "tx" and "rx" + +For more details on the 'dma', 'dma-names', 'clock' and 'clock-names' properties +please check: + * resource-names.txt + * clock/clock-bindings.txt + * dma/dma.txt + +Example: + i2s0: i2s0@0b005000 { + #sound-dai-cells = <0>; + compatible = "zte,zx296702-i2s"; + reg = <0x0b005000 0x1000>; + clocks = <&lsp0clk ZX296702_I2S0_DIV>; + clock-names = "tx"; + interrupts = ; + dmas = <&dma 5>, <&dma 6>; + dma-names = "tx", "rx"; + status = "okay"; + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "zx296702_snd"; + simple-audio-card,format = "left_j"; + simple-audio-card,bitclock-master = <&sndcodec>; + simple-audio-card,frame-master = <&sndcodec>; + sndcpu: simple-audio-card,cpu { + sound-dai = <&i2s0>; + }; + + sndcodec: simple-audio-card,codec { + sound-dai = <&acodec>; + }; + }; -- cgit v1.2.3 From 21e397bd90c303ea9fe18a1f63ddfa67c48f7150 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 8 Jun 2015 15:19:56 +0300 Subject: ASoC: tas2552: Update DT binding document regarding clock configuration Add overview of tas2552's clock configuration and selection. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/tas2552.txt | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/tas2552.txt b/Documentation/devicetree/bindings/sound/tas2552.txt index 55e2a0af5645..c49992c0b62a 100644 --- a/Documentation/devicetree/bindings/sound/tas2552.txt +++ b/Documentation/devicetree/bindings/sound/tas2552.txt @@ -14,6 +14,12 @@ Required properties: Optional properties: - enable-gpio - gpio pin to enable/disable the device +tas2552 can receive it's reference clock via MCLK, BCLK, IVCLKIN pin or use the +internal 1.8MHz. This CLKIN is used by the PLL. In addition to PLL, the PDM +reference clock is also selectable: PLL, IVCLKIN, BCLK or MCLK. +For system integration the dt-bindings/sound/tas2552.h header file provides +defined values to selct and configure the PLL and PDM reference clocks. + Example: tas2552: tas2552@41 { -- cgit v1.2.3 From 8721fa692bc218df3f0ad30740baedf176a41f56 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Wed, 10 Jun 2015 00:17:42 +0900 Subject: Doc: sound:oss: Fix typo in sound/oss This patch fix some spelling typo found in Documentations/sound/oss. Signed-off-by: Masanari Iida Signed-off-by: Takashi Iwai --- Documentation/sound/oss/PSS-updates | 2 +- Documentation/sound/oss/README.OSS | 2 +- Documentation/sound/oss/btaudio | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/oss/PSS-updates b/Documentation/sound/oss/PSS-updates index c84dd7597e64..11914a1dc7e7 100644 --- a/Documentation/sound/oss/PSS-updates +++ b/Documentation/sound/oss/PSS-updates @@ -41,7 +41,7 @@ pss_no_sound This module parameter is a flag that can be used to tell the driver to just configure non-sound components. 0 configures all components, a non-0 -value will only attept to configure the CDROM and joystick ports. This +value will only attempt to configure the CDROM and joystick ports. This parameter can be used by a user who only wished to use the builtin joystick and/or CDROM port(s) of his PSS sound card. If this driver is loaded with this parameter and with the parameter below set to true then a user can safely unload diff --git a/Documentation/sound/oss/README.OSS b/Documentation/sound/oss/README.OSS index 4be259428a1c..a085ea3611a1 100644 --- a/Documentation/sound/oss/README.OSS +++ b/Documentation/sound/oss/README.OSS @@ -1346,7 +1346,7 @@ implement nice real-time signal processing audio effect software and network telephones. The ACI mixer has to be switched into the "solo" mode for duplex operation in order to avoid feedback caused by the mixer (input hears output signal). You can de-/activate this mode -through toggleing the record button for the wave controller with an +through toggling the record button for the wave controller with an OSS-mixer. The PCM20 contains a radio tuner, which is also controlled by diff --git a/Documentation/sound/oss/btaudio b/Documentation/sound/oss/btaudio index 1a693e69d44b..effdb9a3f898 100644 --- a/Documentation/sound/oss/btaudio +++ b/Documentation/sound/oss/btaudio @@ -29,7 +29,7 @@ Driver Status Still somewhat experimental. The driver should work stable, i.e. it should'nt crash your box. It might not work as expected, have bugs, -not being fully OSS API compilant, ... +not being fully OSS API compliant, ... Latest versions are available from http://bytesex.org/bttv/, the driver is in the bttv tarball. Kernel patches might be available too, -- cgit v1.2.3 From 4acf6d7f6837c12551c37e8c9bfc129d686a18c6 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 10 Jun 2015 13:15:44 +0100 Subject: ASoC: qcom: document apq8016 sbc machine driver bindings This patch adds bindings for apq8016 sbc machine driver. APQ8016 has 4 MI2S which can be configured to different sinks like internal codec/external codec, this connection and various parameters are controlled via 2 iomux registers. Acked-by: Kenneth Westfield Signed-off-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/qcom,apq8016-sbc.txt | 60 ++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/qcom,apq8016-sbc.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/qcom,apq8016-sbc.txt b/Documentation/devicetree/bindings/sound/qcom,apq8016-sbc.txt new file mode 100644 index 000000000000..48129368d4d9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,apq8016-sbc.txt @@ -0,0 +1,60 @@ +* Qualcomm Technologies APQ8016 SBC ASoC machine driver + +This node models the Qualcomm Technologies APQ8016 SBC ASoC machine driver + +Required properties: + +- compatible : "qcom,apq8016-sbc-sndcard" + +- pinctrl-N : One property must exist for each entry in + pinctrl-names. See ../pinctrl/pinctrl-bindings.txt + for details of the property values. +- pinctrl-names : Must contain a "default" entry. +- reg : Must contain an address for each entry in reg-names. +- reg-names : A list which must include the following entries: + * "mic-iomux" + * "spkr-iomux" +- qcom,model : Name of the sound card. + +Dai-link subnode properties and subnodes: + +Required dai-link subnodes: + +- cpu : CPU sub-node +- codec : CODEC sub-node + +Required CPU/CODEC subnodes properties: + +-link-name : Name of the dai link. +-sound-dai : phandle and port of CPU/CODEC +-capture-dai : phandle and port of CPU/CODEC + +Example: + +sound: sound { + compatible = "qcom,apq8016-sbc-sndcard"; + reg = <0x07702000 0x4>, <0x07702004 0x4>; + reg-names = "mic-iomux", "spkr-iomux"; + qcom,model = "DB410c"; + + /* I2S - Internal codec */ + internal-dai-link@0 { + cpu { /* PRIMARY */ + sound-dai = <&lpass MI2S_PRIMARY>; + }; + codec { + sound-dai = <&wcd_codec 0>; + }; + }; + + /* External Primary or External Secondary -ADV7533 HDMI */ + external-dai-link@0 { + link-name = "ADV7533"; + cpu { /* QUAT */ + sound-dai = <&lpass MI2S_QUATERNARY>; + }; + codec { + sound-dai = <&adv_bridge 0>; + }; + }; +}; -- cgit v1.2.3 From fb5ab0e7473d41e2a9db0fcb569faf337d595838 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Wed, 10 Jun 2015 14:27:58 +0800 Subject: ASoC: rt5645: add device tree support Modify the RT5645 driver to parse platform data from device tree. Write a DT binding document to describe those properties. Signed-off-by: Bard Liao Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/rt5645.txt | 72 ++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/rt5645.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/rt5645.txt b/Documentation/devicetree/bindings/sound/rt5645.txt new file mode 100644 index 000000000000..7cee1f518f59 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rt5645.txt @@ -0,0 +1,72 @@ +RT5650/RT5645 audio CODEC + +This device supports I2C only. + +Required properties: + +- compatible : One of "realtek,rt5645" or "realtek,rt5650". + +- reg : The I2C address of the device. + +- interrupts : The CODEC's interrupt output. + +Optional properties: + +- hp-detect-gpios: + a GPIO spec for the external headphone detect pin. If jd-mode = 0, + we will get the JD status by getting the value of hp-detect-gpios. + +- realtek,in2-differential + Boolean. Indicate MIC2 input are differential, rather than single-ended. + +- realtek,dmic1-data-pin + 0: dmic1 is not used + 1: using IN2P pin as dmic1 data pin + 2: using GPIO6 pin as dmic1 data pin + 3: using GPIO10 pin as dmic1 data pin + 4: using GPIO12 pin as dmic1 data pin + +- realtek,dmic2-data-pin + 0: dmic2 is not used + 1: using IN2N pin as dmic2 data pin + 2: using GPIO5 pin as dmic2 data pin + 3: using GPIO11 pin as dmic2 data pin + +-- realtek,jd-mode : The JD mode of rt5645/rt5650 + 0 : rt5645/rt5650 JD function is not used + 1 : Mode-0 (VDD=3.3V), two port jack detection + 2 : Mode-1 (VDD=3.3V), one port jack detection + 3 : Mode-2 (VDD=1.8V), one port jack detection + +Pins on the device (for linking into audio routes) for RT5645/RT5650: + + * DMIC L1 + * DMIC R1 + * DMIC L2 + * DMIC R2 + * IN1P + * IN1N + * IN2P + * IN2N + * Haptic Generator + * HPOL + * HPOR + * LOUTL + * LOUTR + * PDM1L + * PDM1R + * SPOL + * SPOR + +Example: + +codec: rt5650@1a { + compatible = "realtek,rt5650"; + reg = <0x1a>; + hp-detect-gpios = <&gpio 19 0>; + interrupt-parent = <&gpio>; + interrupts = <7 IRQ_TYPE_EDGE_FALLING>; + realtek,dmic-en = "true"; + realtek,en-jd-func = "true"; + realtek,jd-mode = <3>; +}; \ No newline at end of file -- cgit v1.2.3 From 85a4bfd895778960dc2d655087ac7ff442b6ab9e Mon Sep 17 00:00:00 2001 From: Arnaud Pouliquen Date: Fri, 5 Jun 2015 10:19:05 +0200 Subject: ASoC: simple card: Add mclk-fs property in dai-link Add mclk-fs ratio property per dai-link sub node. This will allow to manage several codecs with different ratio. Signed-off-by: Arnaud Pouliquen Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/simple-card.txt | 6 +++++- sound/soc/generic/simple-card.c | 18 +++++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/simple-card.txt b/Documentation/devicetree/bindings/sound/simple-card.txt index 73bf314f7240..cf3979eb3578 100644 --- a/Documentation/devicetree/bindings/sound/simple-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-card.txt @@ -16,7 +16,8 @@ Optional properties: connection's sink, the second being the connection's source. - simple-audio-card,mclk-fs : Multiplication factor between stream rate and codec - mclk. + mclk. When defined, mclk-fs property defined in + dai-link sub nodes are ignored. - simple-audio-card,hp-det-gpio : Reference to GPIO that signals when headphones are attached. - simple-audio-card,mic-det-gpio : Reference to GPIO that signals when @@ -55,6 +56,9 @@ Optional dai-link subnode properties: dai-link uses bit clock inversion. - frame-inversion : bool property. Add this if the dai-link uses frame clock inversion. +- mclk-fs : Multiplication factor between stream + rate and codec mclk, applied only for + the dai-link. For backward compatibility the frame-master and bitclock-master properties can be used as booleans in codec subnode to indicate if the diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index c87e58504a62..d5554939146e 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -26,6 +26,7 @@ struct simple_card_data { struct simple_dai_props { struct asoc_simple_dai cpu_dai; struct asoc_simple_dai codec_dai; + unsigned int mclk_fs; } *dai_props; unsigned int mclk_fs; int gpio_hp_det; @@ -76,11 +77,18 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - unsigned int mclk; + struct simple_dai_props *dai_props = + &priv->dai_props[rtd - rtd->card->rtd]; + unsigned int mclk, mclk_fs = 0; int ret = 0; - if (priv->mclk_fs) { - mclk = params_rate(params) * priv->mclk_fs; + if (priv->mclk_fs) + mclk_fs = priv->mclk_fs; + else if (dai_props->mclk_fs) + mclk_fs = dai_props->mclk_fs; + + if (mclk_fs) { + mclk = params_rate(params) * mclk_fs; ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, SND_SOC_CLOCK_IN); } @@ -313,6 +321,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, char prop[128]; char *prefix = ""; int ret, cpu_args; + u32 val; /* For single DAI link & old style of DT node */ if (is_top_level_node) @@ -338,6 +347,9 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, if (ret < 0) goto dai_link_of_err; + if (!of_property_read_u32(node, "mclk-fs", &val)) + dai_props->mclk_fs = val; + ret = asoc_simple_card_sub_parse_of(cpu, &dai_props->cpu_dai, &dai_link->cpu_of_node, &dai_link->cpu_dai_name, -- cgit v1.2.3 From ee0bcaff109f36d582df9851f204c9a5eb79c028 Mon Sep 17 00:00:00 2001 From: Koro Chen Date: Mon, 15 Jun 2015 22:38:02 +0800 Subject: ASoC: mediatek: Add AFE platform driver This is the DPCM based platform driver of AFE (Audio Front End) unit. Signed-off-by: Sascha Hauer Signed-off-by: Koro Chen Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/mtk-afe-pcm.txt | 45 + sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/mediatek/Kconfig | 9 + sound/soc/mediatek/Makefile | 2 + sound/soc/mediatek/mtk-afe-common.h | 109 ++ sound/soc/mediatek/mtk-afe-pcm.c | 1233 ++++++++++++++++++++ 7 files changed, 1400 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/mtk-afe-pcm.txt create mode 100644 sound/soc/mediatek/Kconfig create mode 100644 sound/soc/mediatek/Makefile create mode 100644 sound/soc/mediatek/mtk-afe-common.h create mode 100644 sound/soc/mediatek/mtk-afe-pcm.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/mtk-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mtk-afe-pcm.txt new file mode 100644 index 000000000000..e302c7f43b95 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mtk-afe-pcm.txt @@ -0,0 +1,45 @@ +Mediatek AFE PCM controller + +Required properties: +- compatible = "mediatek,mt8173-afe-pcm"; +- reg: register location and size +- interrupts: Should contain AFE interrupt +- clock-names: should have these clock names: + "infra_sys_audio_clk", + "top_pdn_audio", + "top_pdn_aud_intbus", + "bck0", + "bck1", + "i2s0_m", + "i2s1_m", + "i2s2_m", + "i2s3_m", + "i2s3_b"; + +Example: + + afe: mt8173-afe-pcm@11220000 { + compatible = "mediatek,mt8173-afe-pcm"; + reg = <0 0x11220000 0 0x1000>; + interrupts = ; + clocks = <&infracfg INFRA_AUDIO>, + <&topckgen TOP_AUDIO_SEL>, + <&topckgen TOP_AUD_INTBUS_SEL>, + <&topckgen TOP_APLL1_DIV0>, + <&topckgen TOP_APLL2_DIV0>, + <&topckgen TOP_I2S0_M_CK_SEL>, + <&topckgen TOP_I2S1_M_CK_SEL>, + <&topckgen TOP_I2S2_M_CK_SEL>, + <&topckgen TOP_I2S3_M_CK_SEL>, + <&topckgen TOP_I2S3_B_CK_SEL>; + clock-names = "infra_sys_audio_clk", + "top_pdn_audio", + "top_pdn_aud_intbus", + "bck0", + "bck1", + "i2s0_m", + "i2s1_m", + "i2s2_m", + "i2s3_m", + "i2s3_b"; + }; diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 3ba52da18bc6..cc1b71875b26 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -45,6 +45,7 @@ source "sound/soc/nuc900/Kconfig" source "sound/soc/omap/Kconfig" source "sound/soc/kirkwood/Kconfig" source "sound/soc/intel/Kconfig" +source "sound/soc/mediatek/Kconfig" source "sound/soc/mxs/Kconfig" source "sound/soc/pxa/Kconfig" source "sound/soc/qcom/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 974ba708b482..e5526338aeda 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_SND_SOC) += dwc/ obj-$(CONFIG_SND_SOC) += fsl/ obj-$(CONFIG_SND_SOC) += jz4740/ obj-$(CONFIG_SND_SOC) += intel/ +obj-$(CONFIG_SND_SOC) += mediatek/ obj-$(CONFIG_SND_SOC) += mxs/ obj-$(CONFIG_SND_SOC) += nuc900/ obj-$(CONFIG_SND_SOC) += omap/ diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig new file mode 100644 index 000000000000..c622280cacd9 --- /dev/null +++ b/sound/soc/mediatek/Kconfig @@ -0,0 +1,9 @@ +config SND_SOC_MEDIATEK + tristate "ASoC support for Mediatek chip" + depends on ARCH_MEDIATEK + help + This adds ASoC platform driver support for Mediatek chip + that can be used with other codecs. + Select Y if you have such device. + Ex: MT8173 + diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile new file mode 100644 index 000000000000..5f27cc772f66 --- /dev/null +++ b/sound/soc/mediatek/Makefile @@ -0,0 +1,2 @@ +# MTK Platform Support +obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mtk-afe-common.h new file mode 100644 index 000000000000..a88b17511fdf --- /dev/null +++ b/sound/soc/mediatek/mtk-afe-common.h @@ -0,0 +1,109 @@ +/* + * mtk_afe_common.h -- Mediatek audio driver common definitions + * + * Copyright (c) 2015 MediaTek Inc. + * Author: Koro Chen + * Sascha Hauer + * Hidalgo Huang + * Ir Lian + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef _MTK_AFE_COMMON_H_ +#define _MTK_AFE_COMMON_H_ + +#include +#include + +enum { + MTK_AFE_MEMIF_DL1, + MTK_AFE_MEMIF_DL2, + MTK_AFE_MEMIF_VUL, + MTK_AFE_MEMIF_DAI, + MTK_AFE_MEMIF_AWB, + MTK_AFE_MEMIF_MOD_DAI, + MTK_AFE_MEMIF_HDMI, + MTK_AFE_MEMIF_NUM, + MTK_AFE_IO_MOD_PCM1 = MTK_AFE_MEMIF_NUM, + MTK_AFE_IO_MOD_PCM2, + MTK_AFE_IO_PMIC, + MTK_AFE_IO_I2S, + MTK_AFE_IO_2ND_I2S, + MTK_AFE_IO_HW_GAIN1, + MTK_AFE_IO_HW_GAIN2, + MTK_AFE_IO_MRG_O, + MTK_AFE_IO_MRG_I, + MTK_AFE_IO_DAIBT, + MTK_AFE_IO_HDMI, +}; + +enum { + MTK_AFE_IRQ_1, + MTK_AFE_IRQ_2, + MTK_AFE_IRQ_3, + MTK_AFE_IRQ_4, + MTK_AFE_IRQ_5, + MTK_AFE_IRQ_6, + MTK_AFE_IRQ_7, + MTK_AFE_IRQ_8, + MTK_AFE_IRQ_NUM, +}; + +enum { + MTK_CLK_INFRASYS_AUD, + MTK_CLK_TOP_PDN_AUD, + MTK_CLK_TOP_PDN_AUD_BUS, + MTK_CLK_I2S0_M, + MTK_CLK_I2S1_M, + MTK_CLK_I2S2_M, + MTK_CLK_I2S3_M, + MTK_CLK_I2S3_B, + MTK_CLK_BCK0, + MTK_CLK_BCK1, + MTK_CLK_NUM +}; + +struct mtk_afe; +struct snd_pcm_substream; + +struct mtk_afe_memif_data { + int id; + const char *name; + int reg_ofs_base; + int reg_ofs_cur; + int fs_shift; + int mono_shift; + int enable_shift; + int irq_reg_cnt; + int irq_cnt_shift; + int irq_en_shift; + int irq_fs_shift; + int irq_clr_shift; +}; + +struct mtk_afe_memif { + unsigned int phys_buf_addr; + int buffer_size; + unsigned int hw_ptr; /* Previous IRQ's HW ptr */ + struct snd_pcm_substream *substream; + const struct mtk_afe_memif_data *data; + const struct mtk_afe_irq_data *irqdata; +}; + +struct mtk_afe { + /* address for ioremap audio hardware register */ + void __iomem *base_addr; + struct device *dev; + struct regmap *regmap; + struct mtk_afe_memif memif[MTK_AFE_MEMIF_NUM]; + struct clk *clocks[MTK_CLK_NUM]; +}; +#endif diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mtk-afe-pcm.c new file mode 100644 index 000000000000..cc228db5fb76 --- /dev/null +++ b/sound/soc/mediatek/mtk-afe-pcm.c @@ -0,0 +1,1233 @@ +/* + * Mediatek ALSA SoC AFE platform driver + * + * Copyright (c) 2015 MediaTek Inc. + * Author: Koro Chen + * Sascha Hauer + * Hidalgo Huang + * Ir Lian + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include +#include +#include "mtk-afe-common.h" + +/***************************************************************************** + * R E G I S T E R D E F I N I T I O N + *****************************************************************************/ +#define AUDIO_TOP_CON0 0x0000 +#define AUDIO_TOP_CON1 0x0004 +#define AFE_DAC_CON0 0x0010 +#define AFE_DAC_CON1 0x0014 +#define AFE_I2S_CON1 0x0034 +#define AFE_I2S_CON2 0x0038 +#define AFE_CONN_24BIT 0x006c + +#define AFE_CONN1 0x0024 +#define AFE_CONN2 0x0028 +#define AFE_CONN7 0x0460 +#define AFE_CONN8 0x0464 +#define AFE_HDMI_CONN0 0x0390 + +/* Memory interface */ +#define AFE_DL1_BASE 0x0040 +#define AFE_DL1_CUR 0x0044 +#define AFE_DL2_BASE 0x0050 +#define AFE_DL2_CUR 0x0054 +#define AFE_AWB_BASE 0x0070 +#define AFE_AWB_CUR 0x007c +#define AFE_VUL_BASE 0x0080 +#define AFE_VUL_CUR 0x008c +#define AFE_DAI_BASE 0x0090 +#define AFE_DAI_CUR 0x009c +#define AFE_MOD_PCM_BASE 0x0330 +#define AFE_MOD_PCM_CUR 0x033c +#define AFE_HDMI_OUT_BASE 0x0374 +#define AFE_HDMI_OUT_CUR 0x0378 + +#define AFE_ADDA2_TOP_CON0 0x0600 + +#define AFE_HDMI_OUT_CON0 0x0370 + +#define AFE_IRQ_MCU_CON 0x03a0 +#define AFE_IRQ_STATUS 0x03a4 +#define AFE_IRQ_CLR 0x03a8 +#define AFE_IRQ_CNT1 0x03ac +#define AFE_IRQ_CNT2 0x03b0 +#define AFE_IRQ_MCU_EN 0x03b4 +#define AFE_IRQ_CNT5 0x03bc +#define AFE_IRQ_CNT7 0x03dc + +#define AFE_TDM_CON1 0x0548 +#define AFE_TDM_CON2 0x054c + +#define AFE_BASE_END_OFFSET 8 +#define AFE_IRQ_STATUS_BITS 0xff + +/* AUDIO_TOP_CON0 (0x0000) */ +#define AUD_TCON0_PDN_SPDF (0x1 << 21) +#define AUD_TCON0_PDN_HDMI (0x1 << 20) +#define AUD_TCON0_PDN_24M (0x1 << 9) +#define AUD_TCON0_PDN_22M (0x1 << 8) +#define AUD_TCON0_PDN_AFE (0x1 << 2) + +/* AFE_I2S_CON1 (0x0034) */ +#define AFE_I2S_CON1_LOW_JITTER_CLK (0x1 << 12) +#define AFE_I2S_CON1_RATE(x) (((x) & 0xf) << 8) +#define AFE_I2S_CON1_FORMAT_I2S (0x1 << 3) +#define AFE_I2S_CON1_EN (0x1 << 0) + +/* AFE_I2S_CON2 (0x0038) */ +#define AFE_I2S_CON2_LOW_JITTER_CLK (0x1 << 12) +#define AFE_I2S_CON2_RATE(x) (((x) & 0xf) << 8) +#define AFE_I2S_CON2_FORMAT_I2S (0x1 << 3) +#define AFE_I2S_CON2_EN (0x1 << 0) + +/* AFE_CONN_24BIT (0x006c) */ +#define AFE_CONN_24BIT_O04 (0x1 << 4) +#define AFE_CONN_24BIT_O03 (0x1 << 3) + +/* AFE_HDMI_CONN0 (0x0390) */ +#define AFE_HDMI_CONN0_O37_I37 (0x7 << 21) +#define AFE_HDMI_CONN0_O36_I36 (0x6 << 18) +#define AFE_HDMI_CONN0_O35_I33 (0x3 << 15) +#define AFE_HDMI_CONN0_O34_I32 (0x2 << 12) +#define AFE_HDMI_CONN0_O33_I35 (0x5 << 9) +#define AFE_HDMI_CONN0_O32_I34 (0x4 << 6) +#define AFE_HDMI_CONN0_O31_I31 (0x1 << 3) +#define AFE_HDMI_CONN0_O30_I30 (0x0 << 0) + +/* AFE_TDM_CON1 (0x0548) */ +#define AFE_TDM_CON1_LRCK_WIDTH(x) (((x) - 1) << 24) +#define AFE_TDM_CON1_32_BCK_CYCLES (0x2 << 12) +#define AFE_TDM_CON1_WLEN_32BIT (0x2 << 8) +#define AFE_TDM_CON1_MSB_ALIGNED (0x1 << 4) +#define AFE_TDM_CON1_1_BCK_DELAY (0x1 << 3) +#define AFE_TDM_CON1_BCK_INV (0x1 << 1) +#define AFE_TDM_CON1_EN (0x1 << 0) + +enum afe_tdm_ch_start { + AFE_TDM_CH_START_O30_O31 = 0, + AFE_TDM_CH_START_O32_O33, + AFE_TDM_CH_START_O34_O35, + AFE_TDM_CH_START_O36_O37, + AFE_TDM_CH_ZERO, +}; + +static const struct snd_pcm_hardware mtk_afe_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .buffer_bytes_max = 256 * 1024, + .period_bytes_min = 512, + .period_bytes_max = 128 * 1024, + .periods_min = 2, + .periods_max = 256, + .fifo_size = 0, +}; + +static snd_pcm_uframes_t mtk_afe_pcm_pointer + (struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + + return bytes_to_frames(substream->runtime, memif->hw_ptr); +} + +static const struct snd_pcm_ops mtk_afe_pcm_ops = { + .ioctl = snd_pcm_lib_ioctl, + .pointer = mtk_afe_pcm_pointer, +}; + +static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + size_t size; + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + + size = mtk_afe_hardware.buffer_bytes_max; + + return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + card->dev, size, size); +} + +static void mtk_afe_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static const struct snd_soc_platform_driver mtk_afe_pcm_platform = { + .ops = &mtk_afe_pcm_ops, + .pcm_new = mtk_afe_pcm_new, + .pcm_free = mtk_afe_pcm_free, +}; + +struct mtk_afe_rate { + unsigned int rate; + unsigned int regvalue; +}; + +static const struct mtk_afe_rate mtk_afe_i2s_rates[] = { + { .rate = 8000, .regvalue = 0 }, + { .rate = 11025, .regvalue = 1 }, + { .rate = 12000, .regvalue = 2 }, + { .rate = 16000, .regvalue = 4 }, + { .rate = 22050, .regvalue = 5 }, + { .rate = 24000, .regvalue = 6 }, + { .rate = 32000, .regvalue = 8 }, + { .rate = 44100, .regvalue = 9 }, + { .rate = 48000, .regvalue = 10 }, + { .rate = 88000, .regvalue = 11 }, + { .rate = 96000, .regvalue = 12 }, + { .rate = 174000, .regvalue = 13 }, + { .rate = 192000, .regvalue = 14 }, +}; + +static int mtk_afe_i2s_fs(unsigned int sample_rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mtk_afe_i2s_rates); i++) + if (mtk_afe_i2s_rates[i].rate == sample_rate) + return mtk_afe_i2s_rates[i].regvalue; + + return -EINVAL; +} + +static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate) +{ + unsigned int val; + int fs = mtk_afe_i2s_fs(rate); + + if (fs < 0) + return -EINVAL; + + /* from external ADC */ + regmap_update_bits(afe->regmap, AFE_ADDA2_TOP_CON0, 0x1, 0x1); + + /* set input */ + val = AFE_I2S_CON2_LOW_JITTER_CLK | + AFE_I2S_CON2_RATE(fs) | + AFE_I2S_CON2_FORMAT_I2S; + + regmap_update_bits(afe->regmap, AFE_I2S_CON2, ~AFE_I2S_CON2_EN, val); + + /* set output */ + val = AFE_I2S_CON1_LOW_JITTER_CLK | + AFE_I2S_CON1_RATE(fs) | + AFE_I2S_CON1_FORMAT_I2S; + + regmap_update_bits(afe->regmap, AFE_I2S_CON1, ~AFE_I2S_CON1_EN, val); + return 0; +} + +static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable) +{ + unsigned int val; + + regmap_read(afe->regmap, AFE_I2S_CON2, &val); + if (!!(val & AFE_I2S_CON2_EN) == enable) + return; /* must skip soft reset */ + + /* I2S soft reset begin */ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0x4); + + /* input */ + regmap_update_bits(afe->regmap, AFE_I2S_CON2, 0x1, enable); + + /* output */ + regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable); + + /* I2S soft reset end */ + udelay(1); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0); +} + +static int mtk_afe_dais_enable_clks(struct mtk_afe *afe, + struct clk *m_ck, struct clk *b_ck) +{ + int ret; + + if (m_ck) { + ret = clk_prepare_enable(m_ck); + if (ret) { + dev_err(afe->dev, "Failed to enable m_ck\n"); + return ret; + } + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, 0); + } + + if (b_ck) { + ret = clk_prepare_enable(b_ck); + if (ret) { + dev_err(afe->dev, "Failed to enable b_ck\n"); + return ret; + } + } + return 0; +} + +static int mtk_afe_dais_set_clks(struct mtk_afe *afe, + struct clk *m_ck, unsigned int mck_rate, + struct clk *b_ck, unsigned int bck_rate) +{ + int ret; + + if (m_ck) { + ret = clk_set_rate(m_ck, mck_rate); + if (ret) { + dev_err(afe->dev, "Failed to set m_ck rate\n"); + return ret; + } + } + + if (b_ck) { + ret = clk_set_rate(b_ck, bck_rate); + if (ret) { + dev_err(afe->dev, "Failed to set b_ck rate\n"); + return ret; + } + } + return 0; +} + +static void mtk_afe_dais_disable_clks(struct mtk_afe *afe, + struct clk *m_ck, struct clk *b_ck) +{ + if (m_ck) { + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, + AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M); + clk_disable_unprepare(m_ck); + } + if (b_ck) + clk_disable_unprepare(b_ck); +} + +static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + + if (dai->active) + return 0; + + mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL); + return 0; +} + +static void mtk_afe_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + + if (dai->active) + return; + + mtk_afe_set_i2s_enable(afe, false); + mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL); + + /* disable AFE */ + regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0); +} + +static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime * const runtime = substream->runtime; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + int ret; + + mtk_afe_dais_set_clks(afe, + afe->clocks[MTK_CLK_I2S1_M], runtime->rate * 256, + NULL, 0); + /* config I2S */ + ret = mtk_afe_set_i2s(afe, substream->runtime->rate); + if (ret) + return ret; + + mtk_afe_set_i2s_enable(afe, true); + + return 0; +} + +static int mtk_afe_hdmi_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + + if (dai->active) + return 0; + + mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S3_M], + afe->clocks[MTK_CLK_I2S3_B]); + return 0; +} + +static void mtk_afe_hdmi_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + + if (dai->active) + return; + + mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S3_M], + afe->clocks[MTK_CLK_I2S3_B]); + + /* disable AFE */ + regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0); +} + +static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime * const runtime = substream->runtime; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + unsigned int val; + + mtk_afe_dais_set_clks(afe, + afe->clocks[MTK_CLK_I2S3_M], runtime->rate * 128, + afe->clocks[MTK_CLK_I2S3_B], + runtime->rate * runtime->channels * 32); + + val = AFE_TDM_CON1_BCK_INV | + AFE_TDM_CON1_1_BCK_DELAY | + AFE_TDM_CON1_MSB_ALIGNED | /* I2S mode */ + AFE_TDM_CON1_WLEN_32BIT | + AFE_TDM_CON1_32_BCK_CYCLES | + AFE_TDM_CON1_LRCK_WIDTH(32); + regmap_update_bits(afe->regmap, AFE_TDM_CON1, ~AFE_TDM_CON1_EN, val); + + /* set tdm2 config */ + switch (runtime->channels) { + case 1: + case 2: + val = AFE_TDM_CH_START_O30_O31; + val |= (AFE_TDM_CH_ZERO << 4); + val |= (AFE_TDM_CH_ZERO << 8); + val |= (AFE_TDM_CH_ZERO << 12); + break; + case 3: + case 4: + val = AFE_TDM_CH_START_O30_O31; + val |= (AFE_TDM_CH_START_O32_O33 << 4); + val |= (AFE_TDM_CH_ZERO << 8); + val |= (AFE_TDM_CH_ZERO << 12); + break; + case 5: + case 6: + val = AFE_TDM_CH_START_O30_O31; + val |= (AFE_TDM_CH_START_O32_O33 << 4); + val |= (AFE_TDM_CH_START_O34_O35 << 8); + val |= (AFE_TDM_CH_ZERO << 12); + break; + case 7: + case 8: + val = AFE_TDM_CH_START_O30_O31; + val |= (AFE_TDM_CH_START_O32_O33 << 4); + val |= (AFE_TDM_CH_START_O34_O35 << 8); + val |= (AFE_TDM_CH_START_O36_O37 << 12); + break; + default: + val = 0; + } + regmap_update_bits(afe->regmap, AFE_TDM_CON2, 0x0000ffff, val); + + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, + 0x000000f0, runtime->channels << 4); + return 0; +} + +static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + + dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF, 0); + + /* set connections: O30~O37: L/R/LS/RS/C/LFE/CH7/CH8 */ + regmap_write(afe->regmap, AFE_HDMI_CONN0, + AFE_HDMI_CONN0_O30_I30 | AFE_HDMI_CONN0_O31_I31 | + AFE_HDMI_CONN0_O32_I34 | AFE_HDMI_CONN0_O33_I35 | + AFE_HDMI_CONN0_O34_I32 | AFE_HDMI_CONN0_O35_I33 | + AFE_HDMI_CONN0_O36_I36 | AFE_HDMI_CONN0_O37_I37); + + /* enable Out control */ + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0x1); + + /* enable tdm */ + regmap_update_bits(afe->regmap, AFE_TDM_CON1, 0x1, 0x1); + + return 0; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + /* disable tdm */ + regmap_update_bits(afe->regmap, AFE_TDM_CON1, 0x1, 0); + + /* disable Out control */ + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0); + + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF, + AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF); + + return 0; + default: + return -EINVAL; + } +} + +static int mtk_afe_dais_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + struct snd_pcm_runtime *runtime = substream->runtime; + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + int ret; + + memif->substream = substream; + + snd_soc_set_runtime_hwparams(substream, &mtk_afe_hardware); + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n"); + return ret; +} + +static void mtk_afe_dais_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + + memif->substream = NULL; +} + +static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + int ret; + + dev_dbg(afe->dev, + "%s period = %u, rate= %u, channels=%u\n", + __func__, params_period_size(params), params_rate(params), + params_channels(params)); + + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (ret < 0) + return ret; + + memif->phys_buf_addr = substream->runtime->dma_addr; + memif->buffer_size = substream->runtime->dma_bytes; + memif->hw_ptr = 0; + + /* start */ + regmap_write(afe->regmap, + memif->data->reg_ofs_base, memif->phys_buf_addr); + /* end */ + regmap_write(afe->regmap, + memif->data->reg_ofs_base + AFE_BASE_END_OFFSET, + memif->phys_buf_addr + memif->buffer_size - 1); + + /* set channel */ + if (memif->data->mono_shift >= 0) { + unsigned int mono = (params_channels(params) == 1) ? 1 : 0; + + regmap_update_bits(afe->regmap, AFE_DAC_CON1, + 1 << memif->data->mono_shift, + mono << memif->data->mono_shift); + } + + /* set rate */ + if (memif->data->fs_shift < 0) + return 0; + if (memif->data->id == MTK_AFE_MEMIF_DAI || + memif->data->id == MTK_AFE_MEMIF_MOD_DAI) { + unsigned int val; + + switch (params_rate(params)) { + case 8000: + val = 0; + break; + case 16000: + val = 1; + break; + case 32000: + val = 2; + break; + default: + return -EINVAL; + } + + if (memif->data->id == MTK_AFE_MEMIF_DAI) + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + 0x3 << memif->data->fs_shift, + val << memif->data->fs_shift); + else + regmap_update_bits(afe->regmap, AFE_DAC_CON1, + 0x3 << memif->data->fs_shift, + val << memif->data->fs_shift); + + } else { + int fs = mtk_afe_i2s_fs(params_rate(params)); + + if (fs < 0) + return -EINVAL; + + regmap_update_bits(afe->regmap, AFE_DAC_CON1, + 0xf << memif->data->fs_shift, + fs << memif->data->fs_shift); + } + + return 0; +} + +static int mtk_afe_dais_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int mtk_afe_dais_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + + /* enable AFE */ + regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1); + return 0; +} + +static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime * const runtime = substream->runtime; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + unsigned int counter = runtime->period_size; + + dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (memif->data->enable_shift >= 0) + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + 1 << memif->data->enable_shift, + 1 << memif->data->enable_shift); + + /* set irq counter */ + regmap_update_bits(afe->regmap, + memif->data->irq_reg_cnt, + 0x3ffff << memif->data->irq_cnt_shift, + counter << memif->data->irq_cnt_shift); + + /* set irq fs */ + if (memif->data->irq_fs_shift >= 0) { + int fs = mtk_afe_i2s_fs(runtime->rate); + + if (fs < 0) + return -EINVAL; + + regmap_update_bits(afe->regmap, + AFE_IRQ_MCU_CON, + 0xf << memif->data->irq_fs_shift, + fs << memif->data->irq_fs_shift); + } + /* enable interrupt */ + regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON, + 1 << memif->data->irq_en_shift, + 1 << memif->data->irq_en_shift); + + return 0; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (memif->data->enable_shift >= 0) + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + 1 << memif->data->enable_shift, 0); + /* disable interrupt */ + regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON, + 1 << memif->data->irq_en_shift, + 0 << memif->data->irq_en_shift); + /* and clear pending IRQ */ + regmap_write(afe->regmap, AFE_IRQ_CLR, + 1 << memif->data->irq_clr_shift); + memif->hw_ptr = 0; + return 0; + default: + return -EINVAL; + } +} + +/* FE DAIs */ +static const struct snd_soc_dai_ops mtk_afe_dai_ops = { + .startup = mtk_afe_dais_startup, + .shutdown = mtk_afe_dais_shutdown, + .hw_params = mtk_afe_dais_hw_params, + .hw_free = mtk_afe_dais_hw_free, + .prepare = mtk_afe_dais_prepare, + .trigger = mtk_afe_dais_trigger, +}; + +/* BE DAIs */ +static const struct snd_soc_dai_ops mtk_afe_i2s_ops = { + .startup = mtk_afe_i2s_startup, + .shutdown = mtk_afe_i2s_shutdown, + .prepare = mtk_afe_i2s_prepare, +}; + +static const struct snd_soc_dai_ops mtk_afe_hdmi_ops = { + .startup = mtk_afe_hdmi_startup, + .shutdown = mtk_afe_hdmi_shutdown, + .prepare = mtk_afe_hdmi_prepare, + .trigger = mtk_afe_hdmi_trigger, + +}; + +static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = { + /* FE DAIs: memory intefaces to CPU */ + { + .name = "DL1", /* downlink 1 */ + .id = MTK_AFE_MEMIF_DL1, + .playback = { + .stream_name = "DL1", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_dai_ops, + }, { + .name = "VUL", /* voice uplink */ + .id = MTK_AFE_MEMIF_VUL, + .capture = { + .stream_name = "VUL", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_dai_ops, + }, { + /* BE DAIs */ + .name = "I2S", + .id = MTK_AFE_IO_I2S, + .playback = { + .stream_name = "I2S Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "I2S Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_i2s_ops, + .symmetric_rates = 1, + }, +}; + +static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = { + /* FE DAIs */ + { + .name = "HDMI", + .id = MTK_AFE_MEMIF_HDMI, + .playback = { + .stream_name = "HDMI", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_dai_ops, + }, { + /* BE DAIs */ + .name = "HDMIO", + .id = MTK_AFE_IO_HDMI, + .playback = { + .stream_name = "HDMIO Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_hdmi_ops, + }, +}; + +static const struct snd_kcontrol_new mtk_afe_o03_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN1, 21, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_afe_o04_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN2, 6, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_afe_o09_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN7, 30, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_afe_o10_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN8, 0, 1, 0), +}; + +static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = { + /* Backend DAIs */ + SND_SOC_DAPM_AIF_IN("I2S Capture", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S Playback", NULL, 0, SND_SOC_NOPM, 0, 0), + + /* inter-connections */ + SND_SOC_DAPM_MIXER("I05", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I06", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0, + mtk_afe_o03_mix, ARRAY_SIZE(mtk_afe_o03_mix)), + SND_SOC_DAPM_MIXER("O04", SND_SOC_NOPM, 0, 0, + mtk_afe_o04_mix, ARRAY_SIZE(mtk_afe_o04_mix)), + SND_SOC_DAPM_MIXER("O09", SND_SOC_NOPM, 0, 0, + mtk_afe_o09_mix, ARRAY_SIZE(mtk_afe_o09_mix)), + SND_SOC_DAPM_MIXER("O10", SND_SOC_NOPM, 0, 0, + mtk_afe_o10_mix, ARRAY_SIZE(mtk_afe_o10_mix)), +}; + +static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = { + {"I05", NULL, "DL1"}, + {"I06", NULL, "DL1"}, + {"I2S Playback", NULL, "O03"}, + {"I2S Playback", NULL, "O04"}, + {"VUL", NULL, "O09"}, + {"VUL", NULL, "O10"}, + {"I17", NULL, "I2S Capture"}, + {"I18", NULL, "I2S Capture"}, + { "O03", "I05 Switch", "I05" }, + { "O04", "I06 Switch", "I06" }, + { "O09", "I17 Switch", "I17" }, + { "O10", "I18 Switch", "I18" }, +}; + +static const struct snd_soc_dapm_widget mtk_afe_hdmi_widgets[] = { + /* Backend DAIs */ + SND_SOC_DAPM_AIF_OUT("HDMIO Playback", NULL, 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route mtk_afe_hdmi_routes[] = { + {"HDMIO Playback", NULL, "HDMI"}, +}; + +static const struct snd_soc_component_driver mtk_afe_pcm_dai_component = { + .name = "mtk-afe-pcm-dai", + .dapm_widgets = mtk_afe_pcm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mtk_afe_pcm_widgets), + .dapm_routes = mtk_afe_pcm_routes, + .num_dapm_routes = ARRAY_SIZE(mtk_afe_pcm_routes), +}; + +static const struct snd_soc_component_driver mtk_afe_hdmi_dai_component = { + .name = "mtk-afe-hdmi-dai", + .dapm_widgets = mtk_afe_hdmi_widgets, + .num_dapm_widgets = ARRAY_SIZE(mtk_afe_hdmi_widgets), + .dapm_routes = mtk_afe_hdmi_routes, + .num_dapm_routes = ARRAY_SIZE(mtk_afe_hdmi_routes), +}; + +static const char *aud_clks[MTK_CLK_NUM] = { + [MTK_CLK_INFRASYS_AUD] = "infra_sys_audio_clk", + [MTK_CLK_TOP_PDN_AUD] = "top_pdn_audio", + [MTK_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus", + [MTK_CLK_I2S0_M] = "i2s0_m", + [MTK_CLK_I2S1_M] = "i2s1_m", + [MTK_CLK_I2S2_M] = "i2s2_m", + [MTK_CLK_I2S3_M] = "i2s3_m", + [MTK_CLK_I2S3_B] = "i2s3_b", + [MTK_CLK_BCK0] = "bck0", + [MTK_CLK_BCK1] = "bck1", +}; + +static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { + { + .name = "DL1", + .id = MTK_AFE_MEMIF_DL1, + .reg_ofs_base = AFE_DL1_BASE, + .reg_ofs_cur = AFE_DL1_CUR, + .fs_shift = 0, + .mono_shift = 21, + .enable_shift = 1, + .irq_reg_cnt = AFE_IRQ_CNT1, + .irq_cnt_shift = 0, + .irq_en_shift = 0, + .irq_fs_shift = 4, + .irq_clr_shift = 0, + }, { + .name = "DL2", + .id = MTK_AFE_MEMIF_DL2, + .reg_ofs_base = AFE_DL2_BASE, + .reg_ofs_cur = AFE_DL2_CUR, + .fs_shift = 4, + .mono_shift = 22, + .enable_shift = 2, + .irq_reg_cnt = AFE_IRQ_CNT1, + .irq_cnt_shift = 20, + .irq_en_shift = 2, + .irq_fs_shift = 16, + .irq_clr_shift = 2, + }, { + .name = "VUL", + .id = MTK_AFE_MEMIF_VUL, + .reg_ofs_base = AFE_VUL_BASE, + .reg_ofs_cur = AFE_VUL_CUR, + .fs_shift = 16, + .mono_shift = 27, + .enable_shift = 3, + .irq_reg_cnt = AFE_IRQ_CNT2, + .irq_cnt_shift = 0, + .irq_en_shift = 1, + .irq_fs_shift = 8, + .irq_clr_shift = 1, + }, { + .name = "DAI", + .id = MTK_AFE_MEMIF_DAI, + .reg_ofs_base = AFE_DAI_BASE, + .reg_ofs_cur = AFE_DAI_CUR, + .fs_shift = 24, + .mono_shift = -1, + .enable_shift = 4, + .irq_reg_cnt = AFE_IRQ_CNT2, + .irq_cnt_shift = 20, + .irq_en_shift = 3, + .irq_fs_shift = 20, + .irq_clr_shift = 3, + }, { + .name = "AWB", + .id = MTK_AFE_MEMIF_AWB, + .reg_ofs_base = AFE_AWB_BASE, + .reg_ofs_cur = AFE_AWB_CUR, + .fs_shift = 12, + .mono_shift = 24, + .enable_shift = 6, + .irq_reg_cnt = AFE_IRQ_CNT7, + .irq_cnt_shift = 0, + .irq_en_shift = 14, + .irq_fs_shift = 24, + .irq_clr_shift = 6, + }, { + .name = "MOD_DAI", + .id = MTK_AFE_MEMIF_MOD_DAI, + .reg_ofs_base = AFE_MOD_PCM_BASE, + .reg_ofs_cur = AFE_MOD_PCM_CUR, + .fs_shift = 30, + .mono_shift = 30, + .enable_shift = 7, + .irq_reg_cnt = AFE_IRQ_CNT2, + .irq_cnt_shift = 20, + .irq_en_shift = 3, + .irq_fs_shift = 20, + .irq_clr_shift = 3, + }, { + .name = "HDMI", + .id = MTK_AFE_MEMIF_HDMI, + .reg_ofs_base = AFE_HDMI_OUT_BASE, + .reg_ofs_cur = AFE_HDMI_OUT_CUR, + .fs_shift = -1, + .mono_shift = -1, + .enable_shift = -1, + .irq_reg_cnt = AFE_IRQ_CNT5, + .irq_cnt_shift = 0, + .irq_en_shift = 12, + .irq_fs_shift = -1, + .irq_clr_shift = 4, + }, +}; + +static const struct regmap_config mtk_afe_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = AFE_ADDA2_TOP_CON0, + .cache_type = REGCACHE_NONE, +}; + +static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id) +{ + struct mtk_afe *afe = dev_id; + unsigned int reg_value, hw_ptr; + int i, ret; + + ret = regmap_read(afe->regmap, AFE_IRQ_STATUS, ®_value); + if (ret) { + dev_err(afe->dev, "%s irq status err\n", __func__); + reg_value = AFE_IRQ_STATUS_BITS; + goto err_irq; + } + + for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) { + struct mtk_afe_memif *memif = &afe->memif[i]; + + if (!(reg_value & (1 << memif->data->irq_clr_shift))) + continue; + + ret = regmap_read(afe->regmap, memif->data->reg_ofs_cur, + &hw_ptr); + if (ret || hw_ptr == 0) { + dev_err(afe->dev, "%s hw_ptr err\n", __func__); + hw_ptr = memif->phys_buf_addr; + } + memif->hw_ptr = hw_ptr - memif->phys_buf_addr; + snd_pcm_period_elapsed(memif->substream); + } + +err_irq: + /* clear irq */ + regmap_write(afe->regmap, AFE_IRQ_CLR, reg_value & AFE_IRQ_STATUS_BITS); + + return IRQ_HANDLED; +} + +static int mtk_afe_runtime_suspend(struct device *dev) +{ + struct mtk_afe *afe = dev_get_drvdata(dev); + + /* disable AFE clk */ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE); + + clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]); + clk_disable_unprepare(afe->clocks[MTK_CLK_BCK1]); + clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]); + clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]); + clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]); + return 0; +} + +static int mtk_afe_runtime_resume(struct device *dev) +{ + struct mtk_afe *afe = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_INFRASYS_AUD]); + if (ret) + return ret; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]); + if (ret) + goto err_infra; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD]); + if (ret) + goto err_top_aud_bus; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK0]); + if (ret) + goto err_top_aud; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK1]); + if (ret) + goto err_bck0; + + /* enable AFE clk */ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, AUD_TCON0_PDN_AFE, 0); + + /* set O3/O4 16bits */ + regmap_update_bits(afe->regmap, AFE_CONN_24BIT, + AFE_CONN_24BIT_O03 | AFE_CONN_24BIT_O04, 0); + + /* unmask all IRQs */ + regmap_update_bits(afe->regmap, AFE_IRQ_MCU_EN, 0xff, 0xff); + return 0; + +err_bck0: + clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]); +err_top_aud: + clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]); +err_top_aud_bus: + clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]); +err_infra: + clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]); + return ret; +} + +static int mtk_afe_init_audio_clk(struct mtk_afe *afe) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(aud_clks); i++) { + afe->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]); + if (IS_ERR(afe->clocks[i])) { + dev_err(afe->dev, "%s devm_clk_get %s fail\n", + __func__, aud_clks[i]); + return PTR_ERR(afe->clocks[i]); + } + } + clk_set_rate(afe->clocks[MTK_CLK_BCK0], 22579200); /* 22M */ + clk_set_rate(afe->clocks[MTK_CLK_BCK1], 24576000); /* 24M */ + return 0; +} + +static int mtk_afe_pcm_dev_probe(struct platform_device *pdev) +{ + int ret, i; + unsigned int irq_id; + struct mtk_afe *afe; + struct resource *res; + + afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); + if (!afe) + return -ENOMEM; + + afe->dev = &pdev->dev; + + irq_id = platform_get_irq(pdev, 0); + if (!irq_id) { + dev_err(afe->dev, "np %s no irq\n", afe->dev->of_node->name); + return -ENXIO; + } + ret = devm_request_irq(afe->dev, irq_id, mtk_afe_irq_handler, + 0, "Afe_ISR_Handle", (void *)afe); + if (ret) { + dev_err(afe->dev, "could not request_irq\n"); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + afe->base_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(afe->base_addr)) + return PTR_ERR(afe->base_addr); + + afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr, + &mtk_afe_regmap_config); + if (IS_ERR(afe->regmap)) + return PTR_ERR(afe->regmap); + + /* initial audio related clock */ + ret = mtk_afe_init_audio_clk(afe); + if (ret) { + dev_err(afe->dev, "mtk_afe_init_audio_clk fail\n"); + return ret; + } + + for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) + afe->memif[i].data = &memif_data[i]; + + platform_set_drvdata(pdev, afe); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = mtk_afe_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform); + if (ret) + goto err_pm_disable; + + ret = snd_soc_register_component(&pdev->dev, + &mtk_afe_pcm_dai_component, + mtk_afe_pcm_dais, + ARRAY_SIZE(mtk_afe_pcm_dais)); + if (ret) + goto err_platform; + + ret = snd_soc_register_component(&pdev->dev, + &mtk_afe_hdmi_dai_component, + mtk_afe_hdmi_dais, + ARRAY_SIZE(mtk_afe_hdmi_dais)); + if (ret) + goto err_comp; + + dev_info(&pdev->dev, "MTK AFE driver initialized.\n"); + return 0; + +err_comp: + snd_soc_unregister_component(&pdev->dev); +err_platform: + snd_soc_unregister_platform(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); + return ret; +} + +static int mtk_afe_pcm_dev_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id mtk_afe_pcm_dt_match[] = { + { .compatible = "mediatek,mt8173-afe-pcm", }, + { } +}; +MODULE_DEVICE_TABLE(of, mtk_afe_pcm_dt_match); + +static const struct dev_pm_ops mtk_afe_pm_ops = { + SET_RUNTIME_PM_OPS(mtk_afe_runtime_suspend, mtk_afe_runtime_resume, + NULL) +}; + +static struct platform_driver mtk_afe_pcm_driver = { + .driver = { + .name = "mtk-afe-pcm", + .owner = THIS_MODULE, + .of_match_table = mtk_afe_pcm_dt_match, + .pm = &mtk_afe_pm_ops, + }, + .probe = mtk_afe_pcm_dev_probe, + .remove = mtk_afe_pcm_dev_remove, +}; + +module_platform_driver(mtk_afe_pcm_driver); + +MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver"); +MODULE_AUTHOR("Koro Chen "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From a54f6f0c5b7fb4048ea3de9751720090ac92a570 Mon Sep 17 00:00:00 2001 From: Koro Chen Date: Mon, 15 Jun 2015 22:38:03 +0800 Subject: ASoC: mediatek: Add machine driver for MAX98090 codec This is the DPCM based machine driver with MAX98090 Signed-off-by: Koro Chen Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/mt8173-max98090.txt | 13 ++ sound/soc/mediatek/Kconfig | 10 + sound/soc/mediatek/Makefile | 2 + sound/soc/mediatek/mt8173-max98090.c | 213 +++++++++++++++++++++ 4 files changed, 238 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/mt8173-max98090.txt create mode 100644 sound/soc/mediatek/mt8173-max98090.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/mt8173-max98090.txt b/Documentation/devicetree/bindings/sound/mt8173-max98090.txt new file mode 100644 index 000000000000..829bd26d17f8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mt8173-max98090.txt @@ -0,0 +1,13 @@ +MT8173 with MAX98090 CODEC + +Required properties: +- compatible : "mediatek,mt8173-max98090" +- mediatek,audio-codec: the phandle of the MAX98090 audio codec + +Example: + + sound { + compatible = "mediatek,mt8173-max98090"; + mediatek,audio-codec = <&max98090>; + }; + diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index c622280cacd9..0bfd2a02a408 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -7,3 +7,13 @@ config SND_SOC_MEDIATEK Select Y if you have such device. Ex: MT8173 +config SND_SOC_MT8173_MAX98090 + tristate "ASoC Audio driver for MT8173 with MAX98090 codec" + depends on SND_SOC_MEDIATEK + select SND_SOC_MAX98090 + help + This adds ASoC driver for Mediatek MT8173 boards + with the MAX98090 audio codec. + Select Y if you have such device. + If unsure select "N". + diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile index 5f27cc772f66..08fa765b1f0b 100644 --- a/sound/soc/mediatek/Makefile +++ b/sound/soc/mediatek/Makefile @@ -1,2 +1,4 @@ # MTK Platform Support obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o +# Machine support +obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o diff --git a/sound/soc/mediatek/mt8173-max98090.c b/sound/soc/mediatek/mt8173-max98090.c new file mode 100644 index 000000000000..4d44b5803e55 --- /dev/null +++ b/sound/soc/mediatek/mt8173-max98090.c @@ -0,0 +1,213 @@ +/* + * mt8173-max98090.c -- MT8173 MAX98090 ALSA SoC machine driver + * + * Copyright (c) 2015 MediaTek Inc. + * Author: Koro Chen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include "../codecs/max98090.h" + +static struct snd_soc_jack mt8173_max98090_jack; + +static struct snd_soc_jack_pin mt8173_max98090_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static const struct snd_soc_dapm_widget mt8173_max98090_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route mt8173_max98090_routes[] = { + {"Speaker", NULL, "SPKL"}, + {"Speaker", NULL, "SPKR"}, + {"DMICL", NULL, "Int Mic"}, + {"Headphone", NULL, "HPL"}, + {"Headphone", NULL, "HPR"}, + {"Headset Mic", NULL, "MICBIAS"}, + {"IN34", NULL, "Headset Mic"}, +}; + +static const struct snd_kcontrol_new mt8173_max98090_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static int mt8173_max98090_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 *codec_dai = rtd->codec_dai; + + return snd_soc_dai_set_sysclk(codec_dai, 0, params_rate(params) * 256, + SND_SOC_CLOCK_IN); +} + +static struct snd_soc_ops mt8173_max98090_ops = { + .hw_params = mt8173_max98090_hw_params, +}; + +static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_card *card = runtime->card; + struct snd_soc_codec *codec = runtime->codec; + + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "Headphone", SND_JACK_HEADPHONE, + &mt8173_max98090_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't snd_soc_jack_new %d\n", ret); + return ret; + } + + ret = snd_soc_jack_add_pins(&mt8173_max98090_jack, + ARRAY_SIZE(mt8173_max98090_jack_pins), + mt8173_max98090_jack_pins); + if (ret) { + dev_err(card->dev, "Can't snd_soc_jack_add_pins %d\n", ret); + return ret; + } + + return max98090_mic_detect(codec, &mt8173_max98090_jack); +} + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link mt8173_max98090_dais[] = { + /* Front End DAI links */ + { + .name = "MAX98090 Playback", + .stream_name = "MAX98090 Playback", + .cpu_dai_name = "DL1", + .platform_name = "11220000.mt8173-afe-pcm", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + }, + { + .name = "MAX98090 Capture", + .stream_name = "MAX98090 Capture", + .cpu_dai_name = "VUL", + .platform_name = "11220000.mt8173-afe-pcm", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_capture = 1, + }, + /* Back End DAI links */ + { + .name = "Codec", + .cpu_dai_name = "I2S", + .platform_name = "11220000.mt8173-afe-pcm", + .no_pcm = 1, + .codec_dai_name = "HiFi", + .init = mt8173_max98090_init, + .ops = &mt8173_max98090_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +}; + +static struct snd_soc_card mt8173_max98090_card = { + .name = "mt8173-max98090", + .dai_link = mt8173_max98090_dais, + .num_links = ARRAY_SIZE(mt8173_max98090_dais), + .controls = mt8173_max98090_controls, + .num_controls = ARRAY_SIZE(mt8173_max98090_controls), + .dapm_widgets = mt8173_max98090_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8173_max98090_widgets), + .dapm_routes = mt8173_max98090_routes, + .num_dapm_routes = ARRAY_SIZE(mt8173_max98090_routes), +}; + +static int mt8173_max98090_dev_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt8173_max98090_card; + struct device_node *codec_node; + int ret, i; + + codec_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,audio-codec", 0); + if (!codec_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + for (i = 0; i < card->num_links; i++) { + if (mt8173_max98090_dais[i].codec_name) + continue; + mt8173_max98090_dais[i].codec_of_node = codec_node; + } + card->dev = &pdev->dev; + + ret = snd_soc_register_card(card); + if (ret) + dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", + __func__, ret); + return ret; +} + +static int mt8173_max98090_dev_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + return 0; +} + +static const struct of_device_id mt8173_max98090_dt_match[] = { + { .compatible = "mediatek,mt8173-max98090", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt8173_max98090_dt_match); + +static struct platform_driver mt8173_max98090_driver = { + .driver = { + .name = "mt8173-max98090", + .owner = THIS_MODULE, + .of_match_table = mt8173_max98090_dt_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, + .probe = mt8173_max98090_dev_probe, + .remove = mt8173_max98090_dev_remove, +}; + +module_platform_driver(mt8173_max98090_driver); + +/* Module information */ +MODULE_DESCRIPTION("MT8173 MAX98090 ALSA SoC machine driver"); +MODULE_AUTHOR("Koro Chen "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mt8173-max98090"); + -- cgit v1.2.3 From 662e8d917f685011a24492c106737ea5fdff8136 Mon Sep 17 00:00:00 2001 From: Koro Chen Date: Mon, 15 Jun 2015 22:38:04 +0800 Subject: ASoC: mediatek: Add machine driver for rt5650 rt5676 codec This is the DPCM based machine driver with rt5650 and rt5676 Signed-off-by: Nicolas Boichat Signed-off-by: Koro Chen Signed-off-by: Mark Brown --- .../bindings/sound/mt8173-rt5650-rt5676.txt | 13 + sound/soc/mediatek/Kconfig | 11 + sound/soc/mediatek/Makefile | 1 + sound/soc/mediatek/mt8173-rt5650-rt5676.c | 278 +++++++++++++++++++++ 4 files changed, 303 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt create mode 100644 sound/soc/mediatek/mt8173-rt5650-rt5676.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt b/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt new file mode 100644 index 000000000000..61e98c976bd4 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5676.txt @@ -0,0 +1,13 @@ +MT8173 with RT5650 RT5676 CODECS + +Required properties: +- compatible : "mediatek,mt8173-rt5650-rt5676" +- mediatek,audio-codec: the phandles of rt5650 and rt5676 codecs + +Example: + + sound { + compatible = "mediatek,mt8173-rt5650-rt5676"; + mediatek,audio-codec = <&rt5650 &rt5676>; + }; + diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 0bfd2a02a408..15c04e2eae34 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -17,3 +17,14 @@ config SND_SOC_MT8173_MAX98090 Select Y if you have such device. If unsure select "N". +config SND_SOC_MT8173_RT5650_RT5676 + tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs" + depends on SND_SOC_MEDIATEK + select SND_SOC_RT5645 + select SND_SOC_RT5677 + help + This adds ASoC driver for Mediatek MT8173 boards + with the RT5650 and RT5676 codecs. + Select Y if you have such device. + If unsure select "N". + diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile index 08fa765b1f0b..75effbec438d 100644 --- a/sound/soc/mediatek/Makefile +++ b/sound/soc/mediatek/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o # Machine support obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o +obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c new file mode 100644 index 000000000000..094055323059 --- /dev/null +++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c @@ -0,0 +1,278 @@ +/* + * mt8173-rt5650-rt5676.c -- MT8173 machine driver with RT5650/5676 codecs + * + * Copyright (c) 2015 MediaTek Inc. + * Author: Koro Chen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include +#include "../codecs/rt5645.h" +#include "../codecs/rt5677.h" + +#define MCLK_FOR_CODECS 12288000 + +static const struct snd_soc_dapm_widget mt8173_rt5650_rt5676_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route mt8173_rt5650_rt5676_routes[] = { + {"Speaker", NULL, "SPOL"}, + {"Speaker", NULL, "SPOR"}, + {"Speaker", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650 */ + {"Sub DMIC L1", NULL, "Int Mic"}, /* DMIC from 5676 */ + {"Sub DMIC R1", NULL, "Int Mic"}, + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + {"Headphone", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650 */ + {"Headset Mic", NULL, "micbias1"}, + {"Headset Mic", NULL, "micbias2"}, + {"IN1P", NULL, "Headset Mic"}, + {"IN1N", NULL, "Headset Mic"}, + {"Sub AIF2RX", NULL, "Headset Mic"}, /* IF2 DAC from 5650 */ +}; + +static const struct snd_kcontrol_new mt8173_rt5650_rt5676_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static int mt8173_rt5650_rt5676_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int i, ret; + + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; + + /* pll from mclk 12.288M */ + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS, + params_rate(params) * 512); + if (ret) + return ret; + + /* sysclk from pll */ + ret = snd_soc_dai_set_sysclk(codec_dai, 1, + params_rate(params) * 512, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + return 0; +} + +static struct snd_soc_ops mt8173_rt5650_rt5676_ops = { + .hw_params = mt8173_rt5650_rt5676_hw_params, +}; + +static struct snd_soc_jack mt8173_rt5650_rt5676_jack; + +static int mt8173_rt5650_rt5676_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct snd_soc_codec *codec = runtime->codec_dais[0]->codec; + struct snd_soc_codec *codec_sub = runtime->codec_dais[1]->codec; + int ret; + + rt5645_sel_asrc_clk_src(codec, + RT5645_DA_STEREO_FILTER | + RT5645_AD_STEREO_FILTER, + RT5645_CLK_SEL_I2S1_ASRC); + rt5677_sel_asrc_clk_src(codec_sub, + RT5677_DA_STEREO_FILTER | + RT5677_AD_STEREO1_FILTER, + RT5677_CLK_SEL_I2S1_ASRC); + rt5677_sel_asrc_clk_src(codec_sub, + RT5677_AD_STEREO2_FILTER | + RT5677_I2S2_SOURCE, + RT5677_CLK_SEL_I2S2_ASRC); + + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &mt8173_rt5650_rt5676_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't new Headset Jack %d\n", ret); + return ret; + } + + return rt5645_set_jack_detect(codec, + &mt8173_rt5650_rt5676_jack, + &mt8173_rt5650_rt5676_jack, + &mt8173_rt5650_rt5676_jack); +} + +static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = { + { + .dai_name = "rt5645-aif1", + }, + { + .dai_name = "rt5677-aif1", + }, +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { + /* Front End DAI links */ + { + .name = "rt5650_rt5676 Playback", + .stream_name = "rt5650_rt5676 Playback", + .cpu_dai_name = "DL1", + .platform_name = "11220000.mt8173-afe-pcm", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + }, + { + .name = "rt5650_rt5676 Capture", + .stream_name = "rt5650_rt5676 Capture", + .cpu_dai_name = "VUL", + .platform_name = "11220000.mt8173-afe-pcm", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_capture = 1, + }, + + /* Back End DAI links */ + { + .name = "Codec", + .cpu_dai_name = "I2S", + .platform_name = "11220000.mt8173-afe-pcm", + .no_pcm = 1, + .codecs = mt8173_rt5650_rt5676_codecs, + .num_codecs = 2, + .init = mt8173_rt5650_rt5676_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &mt8173_rt5650_rt5676_ops, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */ + .name = "rt5650_rt5676 intercodec", + .stream_name = "rt5650_rt5676 intercodec", + .cpu_dai_name = "snd-soc-dummy-dai", + .platform_name = "snd-soc-dummy", + .no_pcm = 1, + .codec_dai_name = "rt5677-aif2", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, + }, + +}; + +static struct snd_soc_codec_conf mt8173_rt5650_rt5676_codec_conf[] = { + { + .name_prefix = "Sub", + }, +}; + +static struct snd_soc_card mt8173_rt5650_rt5676_card = { + .name = "mtk-rt5650-rt5676", + .dai_link = mt8173_rt5650_rt5676_dais, + .num_links = ARRAY_SIZE(mt8173_rt5650_rt5676_dais), + .codec_conf = mt8173_rt5650_rt5676_codec_conf, + .num_configs = ARRAY_SIZE(mt8173_rt5650_rt5676_codec_conf), + .controls = mt8173_rt5650_rt5676_controls, + .num_controls = ARRAY_SIZE(mt8173_rt5650_rt5676_controls), + .dapm_widgets = mt8173_rt5650_rt5676_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_rt5676_widgets), + .dapm_routes = mt8173_rt5650_rt5676_routes, + .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_rt5676_routes), +}; + +static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt8173_rt5650_rt5676_card; + int ret; + + mt8173_rt5650_rt5676_codecs[0].of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0); + if (!mt8173_rt5650_rt5676_codecs[0].of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + mt8173_rt5650_rt5676_codecs[1].of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1); + if (!mt8173_rt5650_rt5676_codecs[1].of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + mt8173_rt5650_rt5676_codec_conf[0].of_node = + mt8173_rt5650_rt5676_codecs[1].of_node; + + mt8173_rt5650_rt5676_dais[3].codec_of_node = + mt8173_rt5650_rt5676_codecs[1].of_node; + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + + ret = snd_soc_register_card(card); + if (ret) + dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", + __func__, ret); + return ret; +} + +static int mt8173_rt5650_rt5676_dev_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + return 0; +} + +static const struct of_device_id mt8173_rt5650_rt5676_dt_match[] = { + { .compatible = "mediatek,mt8173-rt5650-rt5676", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt8173_rt5650_rt5676_dt_match); + +static struct platform_driver mt8173_rt5650_rt5676_driver = { + .driver = { + .name = "mtk-rt5650-rt5676", + .owner = THIS_MODULE, + .of_match_table = mt8173_rt5650_rt5676_dt_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, + .probe = mt8173_rt5650_rt5676_dev_probe, + .remove = mt8173_rt5650_rt5676_dev_remove, +}; + +module_platform_driver(mt8173_rt5650_rt5676_driver); + +/* Module information */ +MODULE_DESCRIPTION("MT8173 RT5650 and RT5676 SoC machine driver"); +MODULE_AUTHOR("Koro Chen "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mtk-rt5650-rt5676"); + -- cgit v1.2.3