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