1 // SPDX-License-Identifier: GPL-2.0+ !! 1 /* 2 // !! 2 * Copyright 2012 Freescale Semiconductor, Inc. 3 // Copyright 2012 Freescale Semiconductor, Inc !! 3 * Copyright 2012 Linaro Ltd. 4 // Copyright 2012 Linaro Ltd. !! 4 * >> 5 * The code contained herein is licensed under the GNU General Public >> 6 * License. You may obtain a copy of the GNU General Public License >> 7 * Version 2 or later at the following locations: >> 8 * >> 9 * http://www.opensource.org/licenses/gpl-license.html >> 10 * http://www.gnu.org/copyleft/gpl.html >> 11 */ 5 12 6 #include <linux/gpio/consumer.h> !! 13 #include <linux/gpio.h> 7 #include <linux/module.h> 14 #include <linux/module.h> 8 #include <linux/of.h> 15 #include <linux/of.h> 9 #include <linux/of_platform.h> 16 #include <linux/of_platform.h> 10 #include <linux/i2c.h> 17 #include <linux/i2c.h> >> 18 #include <linux/of_gpio.h> 11 #include <sound/soc.h> 19 #include <sound/soc.h> 12 #include <sound/jack.h> 20 #include <sound/jack.h> 13 21 14 #include "imx-audmux.h" 22 #include "imx-audmux.h" 15 23 16 #define DAI_NAME_SIZE 32 24 #define DAI_NAME_SIZE 32 17 #define MUX_PORT_MAX 7 25 #define MUX_PORT_MAX 7 18 26 19 struct imx_es8328_data { 27 struct imx_es8328_data { 20 struct device *dev; 28 struct device *dev; 21 struct snd_soc_dai_link dai; 29 struct snd_soc_dai_link dai; 22 struct snd_soc_card card; 30 struct snd_soc_card card; 23 char codec_dai_name[DAI_NAME_SIZE]; 31 char codec_dai_name[DAI_NAME_SIZE]; 24 char platform_name[DAI_NAME_SIZE]; 32 char platform_name[DAI_NAME_SIZE]; 25 struct gpio_desc *jack_gpiod; !! 33 int jack_gpio; 26 }; 34 }; 27 35 28 static struct snd_soc_jack_gpio headset_jack_g 36 static struct snd_soc_jack_gpio headset_jack_gpios[] = { 29 { 37 { >> 38 .gpio = -1, 30 .name = "headset-gpio", 39 .name = "headset-gpio", 31 .report = SND_JACK_HEADSET, 40 .report = SND_JACK_HEADSET, 32 .invert = 0, 41 .invert = 0, 33 .debounce_time = 200, 42 .debounce_time = 200, 34 }, 43 }, 35 }; 44 }; 36 45 37 static struct snd_soc_jack headset_jack; 46 static struct snd_soc_jack headset_jack; 38 static struct snd_soc_jack_pin headset_jack_pi << 39 { << 40 .pin = "Headphone", << 41 .mask = SND_JACK_HEADPHONE, << 42 }, << 43 { << 44 .pin = "Mic Jack", << 45 .mask = SND_JACK_MICROPHONE, << 46 }, << 47 }; << 48 47 49 static int imx_es8328_dai_init(struct snd_soc_ 48 static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd) 50 { 49 { 51 struct imx_es8328_data *data = contain 50 struct imx_es8328_data *data = container_of(rtd->card, 52 struct 51 struct imx_es8328_data, card); 53 int ret = 0; 52 int ret = 0; 54 53 55 if (data->jack_gpiod) { !! 54 /* Headphone jack detection */ 56 /* Headphone jack detection */ !! 55 if (gpio_is_valid(data->jack_gpio)) { 57 ret = snd_soc_card_jack_new_pi !! 56 ret = snd_soc_card_jack_new(rtd->card, "Headphone", 58 !! 57 SND_JACK_HEADPHONE | SND_JACK_BTN_0, 59 !! 58 &headset_jack, NULL, 0); 60 << 61 << 62 if (ret) 59 if (ret) 63 return ret; 60 return ret; 64 61 65 headset_jack_gpios[0].desc = d !! 62 headset_jack_gpios[0].gpio = data->jack_gpio; 66 ret = snd_soc_jack_add_gpios(& 63 ret = snd_soc_jack_add_gpios(&headset_jack, 67 A 64 ARRAY_SIZE(headset_jack_gpios), 68 h 65 headset_jack_gpios); 69 } 66 } 70 67 71 return ret; 68 return ret; 72 } 69 } 73 70 74 static const struct snd_soc_dapm_widget imx_es 71 static const struct snd_soc_dapm_widget imx_es8328_dapm_widgets[] = { 75 SND_SOC_DAPM_MIC("Mic Jack", NULL), 72 SND_SOC_DAPM_MIC("Mic Jack", NULL), 76 SND_SOC_DAPM_HP("Headphone", NULL), 73 SND_SOC_DAPM_HP("Headphone", NULL), 77 SND_SOC_DAPM_SPK("Speaker", NULL), 74 SND_SOC_DAPM_SPK("Speaker", NULL), 78 SND_SOC_DAPM_REGULATOR_SUPPLY("audio-a 75 SND_SOC_DAPM_REGULATOR_SUPPLY("audio-amp", 1, 0), 79 }; 76 }; 80 77 81 static const struct snd_kcontrol_new imx_es832 << 82 SOC_DAPM_PIN_SWITCH("Headphone"), << 83 SOC_DAPM_PIN_SWITCH("Mic Jack"), << 84 }; << 85 << 86 static int imx_es8328_probe(struct platform_de 78 static int imx_es8328_probe(struct platform_device *pdev) 87 { 79 { 88 struct device_node *np = pdev->dev.of_ 80 struct device_node *np = pdev->dev.of_node; 89 struct device_node *ssi_np = NULL, *co 81 struct device_node *ssi_np = NULL, *codec_np = NULL; 90 struct platform_device *ssi_pdev; 82 struct platform_device *ssi_pdev; 91 struct imx_es8328_data *data; 83 struct imx_es8328_data *data; 92 struct snd_soc_dai_link_component *com << 93 u32 int_port, ext_port; 84 u32 int_port, ext_port; 94 int ret; 85 int ret; 95 struct device *dev = &pdev->dev; 86 struct device *dev = &pdev->dev; 96 87 97 ret = of_property_read_u32(np, "mux-in 88 ret = of_property_read_u32(np, "mux-int-port", &int_port); 98 if (ret) { 89 if (ret) { 99 dev_err(dev, "mux-int-port mis 90 dev_err(dev, "mux-int-port missing or invalid\n"); 100 goto fail; 91 goto fail; 101 } 92 } 102 if (int_port > MUX_PORT_MAX || int_por 93 if (int_port > MUX_PORT_MAX || int_port == 0) { 103 dev_err(dev, "mux-int-port: ha 94 dev_err(dev, "mux-int-port: hardware only has %d mux ports\n", 104 MUX_PORT_MAX); 95 MUX_PORT_MAX); 105 ret = -EINVAL; << 106 goto fail; 96 goto fail; 107 } 97 } 108 98 109 ret = of_property_read_u32(np, "mux-ex 99 ret = of_property_read_u32(np, "mux-ext-port", &ext_port); 110 if (ret) { 100 if (ret) { 111 dev_err(dev, "mux-ext-port mis 101 dev_err(dev, "mux-ext-port missing or invalid\n"); 112 goto fail; 102 goto fail; 113 } 103 } 114 if (ext_port > MUX_PORT_MAX || ext_por 104 if (ext_port > MUX_PORT_MAX || ext_port == 0) { 115 dev_err(dev, "mux-ext-port: ha 105 dev_err(dev, "mux-ext-port: hardware only has %d mux ports\n", 116 MUX_PORT_MAX); 106 MUX_PORT_MAX); 117 ret = -EINVAL; 107 ret = -EINVAL; 118 goto fail; 108 goto fail; 119 } 109 } 120 110 121 /* 111 /* 122 * The port numbering in the hardware 112 * The port numbering in the hardware manual starts at 1, while 123 * the audmux API expects it starts at 113 * the audmux API expects it starts at 0. 124 */ 114 */ 125 int_port--; 115 int_port--; 126 ext_port--; 116 ext_port--; 127 ret = imx_audmux_v2_configure_port(int 117 ret = imx_audmux_v2_configure_port(int_port, 128 IMX_AUDMUX_V2_PTCR_SYN 118 IMX_AUDMUX_V2_PTCR_SYN | 129 IMX_AUDMUX_V2_PTCR_TFS 119 IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | 130 IMX_AUDMUX_V2_PTCR_TCS 120 IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | 131 IMX_AUDMUX_V2_PTCR_TFS 121 IMX_AUDMUX_V2_PTCR_TFSDIR | 132 IMX_AUDMUX_V2_PTCR_TCL 122 IMX_AUDMUX_V2_PTCR_TCLKDIR, 133 IMX_AUDMUX_V2_PDCR_RXD 123 IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); 134 if (ret) { 124 if (ret) { 135 dev_err(dev, "audmux internal 125 dev_err(dev, "audmux internal port setup failed\n"); 136 return ret; 126 return ret; 137 } 127 } 138 ret = imx_audmux_v2_configure_port(ext 128 ret = imx_audmux_v2_configure_port(ext_port, 139 IMX_AUDMUX_V2_PTCR_SYN 129 IMX_AUDMUX_V2_PTCR_SYN, 140 IMX_AUDMUX_V2_PDCR_RXD 130 IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); 141 if (ret) { 131 if (ret) { 142 dev_err(dev, "audmux external 132 dev_err(dev, "audmux external port setup failed\n"); 143 return ret; 133 return ret; 144 } 134 } 145 135 146 ssi_np = of_parse_phandle(pdev->dev.of 136 ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0); 147 codec_np = of_parse_phandle(pdev->dev. 137 codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0); 148 if (!ssi_np || !codec_np) { 138 if (!ssi_np || !codec_np) { 149 dev_err(dev, "phandle missing 139 dev_err(dev, "phandle missing or invalid\n"); 150 ret = -EINVAL; 140 ret = -EINVAL; 151 goto fail; 141 goto fail; 152 } 142 } 153 143 154 ssi_pdev = of_find_device_by_node(ssi_ 144 ssi_pdev = of_find_device_by_node(ssi_np); 155 if (!ssi_pdev) { 145 if (!ssi_pdev) { 156 dev_err(dev, "failed to find S 146 dev_err(dev, "failed to find SSI platform device\n"); 157 ret = -EINVAL; 147 ret = -EINVAL; 158 goto fail; 148 goto fail; 159 } 149 } 160 150 161 data = devm_kzalloc(dev, sizeof(*data) 151 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 162 if (!data) { 152 if (!data) { 163 ret = -ENOMEM; 153 ret = -ENOMEM; 164 goto put_device; !! 154 goto fail; 165 } << 166 << 167 comp = devm_kzalloc(dev, 2 * sizeof(*c << 168 if (!comp) { << 169 ret = -ENOMEM; << 170 goto put_device; << 171 } 155 } 172 156 173 data->dev = dev; 157 data->dev = dev; 174 158 175 data->jack_gpiod = devm_gpiod_get_opti !! 159 data->jack_gpio = of_get_named_gpio(pdev->dev.of_node, "jack-gpio", 0); 176 if (IS_ERR(data->jack_gpiod)) { << 177 ret = PTR_ERR(data->jack_gpiod << 178 goto put_device; << 179 } << 180 << 181 /* << 182 * CPU == Platform << 183 * platform is using soc-generic-dmaen << 184 */ << 185 data->dai.cpus = << 186 data->dai.platforms = &comp[0]; << 187 data->dai.codecs = &comp[1]; << 188 << 189 data->dai.num_cpus = 1; << 190 data->dai.num_codecs = 1; << 191 data->dai.num_platforms = 1; << 192 160 193 data->dai.name = "hifi"; 161 data->dai.name = "hifi"; 194 data->dai.stream_name = "hifi"; 162 data->dai.stream_name = "hifi"; 195 data->dai.codecs->dai_name = "es8328-h !! 163 data->dai.codec_dai_name = "es8328-hifi-analog"; 196 data->dai.codecs->of_node = codec_np; !! 164 data->dai.codec_of_node = codec_np; 197 data->dai.cpus->of_node = ssi_np; !! 165 data->dai.cpu_of_node = ssi_np; >> 166 data->dai.platform_of_node = ssi_np; 198 data->dai.init = &imx_es8328_dai_init; 167 data->dai.init = &imx_es8328_dai_init; 199 data->dai.dai_fmt = SND_SOC_DAIFMT_I2S 168 data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 200 SND_SOC_DAIFMT_CBP !! 169 SND_SOC_DAIFMT_CBM_CFM; 201 170 202 data->card.dev = dev; 171 data->card.dev = dev; 203 data->card.dapm_widgets = imx_es8328_d 172 data->card.dapm_widgets = imx_es8328_dapm_widgets; 204 data->card.num_dapm_widgets = ARRAY_SI 173 data->card.num_dapm_widgets = ARRAY_SIZE(imx_es8328_dapm_widgets); 205 data->card.controls = imx_es8328_contr << 206 data->card.num_controls = ARRAY_SIZE(i << 207 ret = snd_soc_of_parse_card_name(&data 174 ret = snd_soc_of_parse_card_name(&data->card, "model"); 208 if (ret) { 175 if (ret) { 209 dev_err(dev, "Unable to parse 176 dev_err(dev, "Unable to parse card name\n"); 210 goto put_device; !! 177 goto fail; 211 } 178 } 212 ret = snd_soc_of_parse_audio_routing(& 179 ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); 213 if (ret) { 180 if (ret) { 214 dev_err(dev, "Unable to parse 181 dev_err(dev, "Unable to parse routing: %d\n", ret); 215 goto put_device; !! 182 goto fail; 216 } 183 } 217 data->card.num_links = 1; 184 data->card.num_links = 1; 218 data->card.owner = THIS_MODULE; 185 data->card.owner = THIS_MODULE; 219 data->card.dai_link = &data->dai; 186 data->card.dai_link = &data->dai; 220 187 221 ret = devm_snd_soc_register_card(&pdev !! 188 ret = snd_soc_register_card(&data->card); 222 if (ret) { 189 if (ret) { 223 dev_err(dev, "Unable to regist 190 dev_err(dev, "Unable to register: %d\n", ret); 224 goto put_device; !! 191 goto fail; 225 } 192 } 226 193 227 platform_set_drvdata(pdev, data); 194 platform_set_drvdata(pdev, data); 228 put_device: << 229 put_device(&ssi_pdev->dev); << 230 fail: 195 fail: 231 of_node_put(ssi_np); 196 of_node_put(ssi_np); 232 of_node_put(codec_np); 197 of_node_put(codec_np); 233 198 234 return ret; 199 return ret; 235 } 200 } 236 201 >> 202 static int imx_es8328_remove(struct platform_device *pdev) >> 203 { >> 204 struct imx_es8328_data *data = platform_get_drvdata(pdev); >> 205 >> 206 snd_soc_unregister_card(&data->card); >> 207 >> 208 return 0; >> 209 } >> 210 237 static const struct of_device_id imx_es8328_dt 211 static const struct of_device_id imx_es8328_dt_ids[] = { 238 { .compatible = "fsl,imx-audio-es8328" 212 { .compatible = "fsl,imx-audio-es8328", }, 239 { /* sentinel */ } 213 { /* sentinel */ } 240 }; 214 }; 241 MODULE_DEVICE_TABLE(of, imx_es8328_dt_ids); 215 MODULE_DEVICE_TABLE(of, imx_es8328_dt_ids); 242 216 243 static struct platform_driver imx_es8328_drive 217 static struct platform_driver imx_es8328_driver = { 244 .driver = { 218 .driver = { 245 .name = "imx-es8328", 219 .name = "imx-es8328", 246 .of_match_table = imx_es8328_d 220 .of_match_table = imx_es8328_dt_ids, 247 }, 221 }, 248 .probe = imx_es8328_probe, 222 .probe = imx_es8328_probe, >> 223 .remove = imx_es8328_remove, 249 }; 224 }; 250 module_platform_driver(imx_es8328_driver); 225 module_platform_driver(imx_es8328_driver); 251 226 252 MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>"); 227 MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>"); 253 MODULE_DESCRIPTION("Kosagi i.MX6 ES8328 ASoC m 228 MODULE_DESCRIPTION("Kosagi i.MX6 ES8328 ASoC machine driver"); 254 MODULE_LICENSE("GPL v2"); 229 MODULE_LICENSE("GPL v2"); 255 MODULE_ALIAS("platform:imx-audio-es8328"); 230 MODULE_ALIAS("platform:imx-audio-es8328"); 256 231
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.