Commit | Line | Data |
---|---|---|
c448303e SG |
1 | /* |
2 | * Copyright 2012 Freescale Semiconductor, Inc. | |
3 | * 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 | */ | |
12 | ||
13 | #include <linux/module.h> | |
14 | #include <linux/of.h> | |
15 | #include <linux/of_platform.h> | |
16 | #include <sound/soc.h> | |
17 | ||
18 | #include "../codecs/sgtl5000.h" | |
19 | #include "imx-audmux.h" | |
20 | ||
21 | #define DAI_NAME_SIZE 32 | |
22 | ||
23 | struct imx_sgtl5000_data { | |
24 | struct snd_soc_dai_link dai; | |
25 | struct snd_soc_card card; | |
26 | char codec_dai_name[DAI_NAME_SIZE]; | |
27 | char platform_name[DAI_NAME_SIZE]; | |
28 | unsigned int clk_frequency; | |
29 | }; | |
30 | ||
31 | static int imx_sgtl5000_dai_init(struct snd_soc_pcm_runtime *rtd) | |
32 | { | |
33 | struct imx_sgtl5000_data *data = container_of(rtd->card, | |
34 | struct imx_sgtl5000_data, card); | |
35 | struct device *dev = rtd->card->dev; | |
36 | int ret; | |
37 | ||
38 | ret = snd_soc_dai_set_sysclk(rtd->codec_dai, SGTL5000_SYSCLK, | |
39 | data->clk_frequency, SND_SOC_CLOCK_IN); | |
40 | if (ret) { | |
41 | dev_err(dev, "could not set codec driver clock params\n"); | |
42 | return ret; | |
43 | } | |
44 | ||
45 | return 0; | |
46 | } | |
47 | ||
48 | static int __devinit imx_sgtl5000_probe(struct platform_device *pdev) | |
49 | { | |
50 | struct device_node *np = pdev->dev.of_node; | |
51 | struct device_node *ssi_np, *codec_np; | |
52 | struct platform_device *ssi_pdev; | |
53 | struct imx_sgtl5000_data *data; | |
54 | int int_port, ext_port; | |
55 | int ret; | |
56 | ||
57 | ret = of_property_read_u32(np, "mux-int-port", &int_port); | |
58 | if (ret) { | |
59 | dev_err(&pdev->dev, "mux-int-port missing or invalid\n"); | |
60 | return ret; | |
61 | } | |
62 | ret = of_property_read_u32(np, "mux-ext-port", &ext_port); | |
63 | if (ret) { | |
64 | dev_err(&pdev->dev, "mux-ext-port missing or invalid\n"); | |
65 | return ret; | |
66 | } | |
67 | ||
68 | /* | |
69 | * The port numbering in the hardware manual starts at 1, while | |
70 | * the audmux API expects it starts at 0. | |
71 | */ | |
72 | int_port--; | |
73 | ext_port--; | |
74 | ret = imx_audmux_v2_configure_port(int_port, | |
75 | IMX_AUDMUX_V2_PTCR_SYN | | |
76 | IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | | |
77 | IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | | |
78 | IMX_AUDMUX_V2_PTCR_TFSDIR | | |
79 | IMX_AUDMUX_V2_PTCR_TCLKDIR, | |
80 | IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); | |
81 | if (ret) { | |
82 | dev_err(&pdev->dev, "audmux internal port setup failed\n"); | |
83 | return ret; | |
84 | } | |
85 | imx_audmux_v2_configure_port(ext_port, | |
86 | IMX_AUDMUX_V2_PTCR_SYN | | |
87 | IMX_AUDMUX_V2_PTCR_TCSEL(int_port), | |
88 | IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); | |
89 | if (ret) { | |
90 | dev_err(&pdev->dev, "audmux external port setup failed\n"); | |
91 | return ret; | |
92 | } | |
93 | ||
94 | ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0); | |
95 | codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0); | |
96 | if (!ssi_np || !codec_np) { | |
97 | dev_err(&pdev->dev, "phandle missing or invalid\n"); | |
98 | return -EINVAL; | |
99 | } | |
100 | ||
101 | ssi_pdev = of_find_device_by_node(ssi_np); | |
102 | if (!ssi_pdev) { | |
103 | dev_err(&pdev->dev, "failed to find SSI platform device\n"); | |
104 | return -EINVAL; | |
105 | } | |
106 | ||
107 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); | |
108 | if (!data) | |
109 | return -ENOMEM; | |
110 | ||
111 | ret = of_property_read_u32(codec_np, "clock-frequency", | |
112 | &data->clk_frequency); | |
113 | if (ret) { | |
114 | dev_err(&pdev->dev, "clock-frequency missing or invalid\n"); | |
115 | return ret; | |
116 | } | |
117 | ||
118 | data->dai.name = "HiFi"; | |
119 | data->dai.stream_name = "HiFi"; | |
120 | data->dai.codec_dai_name = "sgtl5000"; | |
121 | data->dai.codec_of_node = codec_np; | |
122 | data->dai.cpu_dai_name = dev_name(&ssi_pdev->dev); | |
123 | data->dai.platform_name = "imx-pcm-audio"; | |
124 | data->dai.init = &imx_sgtl5000_dai_init; | |
125 | data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | |
126 | SND_SOC_DAIFMT_CBM_CFM; | |
127 | ||
128 | data->card.dev = &pdev->dev; | |
129 | ret = snd_soc_of_parse_card_name(&data->card, "model"); | |
130 | if (ret) | |
131 | return ret; | |
132 | data->card.num_links = 1; | |
133 | data->card.dai_link = &data->dai; | |
134 | ||
135 | ret = snd_soc_register_card(&data->card); | |
136 | if (ret) { | |
137 | dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); | |
138 | return ret; | |
139 | } | |
140 | ||
141 | platform_set_drvdata(pdev, data); | |
142 | of_node_put(ssi_np); | |
143 | of_node_put(codec_np); | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
148 | static int __devexit imx_sgtl5000_remove(struct platform_device *pdev) | |
149 | { | |
150 | struct imx_sgtl5000_data *data = platform_get_drvdata(pdev); | |
151 | ||
152 | snd_soc_unregister_card(&data->card); | |
153 | ||
154 | return 0; | |
155 | } | |
156 | ||
157 | static const struct of_device_id imx_sgtl5000_dt_ids[] = { | |
158 | { .compatible = "fsl,imx-audio-sgtl5000", }, | |
159 | { /* sentinel */ } | |
160 | }; | |
161 | MODULE_DEVICE_TABLE(of, imx_sgtl5000_dt_ids); | |
162 | ||
163 | static struct platform_driver imx_sgtl5000_driver = { | |
164 | .driver = { | |
165 | .name = "imx-sgtl5000", | |
166 | .owner = THIS_MODULE, | |
167 | .of_match_table = imx_sgtl5000_dt_ids, | |
168 | }, | |
169 | .probe = imx_sgtl5000_probe, | |
170 | .remove = __devexit_p(imx_sgtl5000_remove), | |
171 | }; | |
172 | module_platform_driver(imx_sgtl5000_driver); | |
173 | ||
174 | MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>"); | |
175 | MODULE_DESCRIPTION("Freescale i.MX SGTL5000 ASoC machine driver"); | |
176 | MODULE_LICENSE("GPL v2"); | |
177 | MODULE_ALIAS("platform:imx-sgtl5000"); |