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