ASoC: hdac-hdmi: Add hdmi driver
[deliverable/linux.git] / sound / soc / codecs / hdac_hdmi.c
CommitLineData
18382ead
SP
1/*
2 * hdac_hdmi.c - ASoc HDA-HDMI codec driver for Intel platforms
3 *
4 * Copyright (C) 2014-2015 Intel Corp
5 * Author: Samreen Nilofer <samreen.nilofer@intel.com>
6 * Subhransu S. Prusty <subhransu.s.prusty@intel.com>
7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19 */
20#include <linux/init.h>
21#include <linux/delay.h>
22#include <linux/module.h>
23#include <linux/pm_runtime.h>
24#include <sound/pcm_params.h>
25#include <sound/soc.h>
26#include <sound/hdaudio_ext.h>
27#include "../../hda/local.h"
28
29#define PIN_OUT (AC_PINCTL_OUT_EN)
30#define HDA_MAX_CONNECTIONS 32
31
32struct hdac_hdmi_cvt_params {
33 unsigned int channels_min;
34 unsigned int channels_max;
35 u32 rates;
36 u64 formats;
37 unsigned int maxbps;
38};
39
40struct hdac_hdmi_cvt {
41 hda_nid_t nid;
42 struct hdac_hdmi_cvt_params params;
43};
44
45struct hdac_hdmi_pin {
46 hda_nid_t nid;
47 int num_mux_nids;
48 hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
49};
50
51struct hdac_hdmi_dai_pin_map {
52 int dai_id;
53 struct hdac_hdmi_pin pin;
54 struct hdac_hdmi_cvt cvt;
55};
56
57struct hdac_hdmi_priv {
58 hda_nid_t pin_nid[3];
59 hda_nid_t cvt_nid[3];
60 struct hdac_hdmi_dai_pin_map dai_map[3];
61};
62
63static int
64hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
65{
66 int err;
67
68 /* Only stereo supported as of now */
69 cvt->params.channels_min = cvt->params.channels_max = 2;
70
71 err = snd_hdac_query_supported_pcm(hdac, cvt->nid,
72 &cvt->params.rates,
73 &cvt->params.formats,
74 &cvt->params.maxbps);
75 if (err < 0)
76 dev_err(&hdac->dev,
77 "Failed to query pcm params for nid %d: %d\n",
78 cvt->nid, err);
79
80 return err;
81}
82
83static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac,
84 struct hdac_hdmi_pin *pin)
85{
86 if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) {
87 dev_warn(&hdac->hdac.dev,
88 "HDMI: pin %d wcaps %#x does not support connection list\n",
89 pin->nid, get_wcaps(&hdac->hdac, pin->nid));
90 return -EINVAL;
91 }
92
93 pin->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid,
94 pin->mux_nids, HDA_MAX_CONNECTIONS);
95 if (pin->num_mux_nids == 0) {
96 dev_err(&hdac->hdac.dev, "No connections found\n");
97 return -ENODEV;
98 }
99
100 return pin->num_mux_nids;
101}
102
103static void hdac_hdmi_fill_widget_info(struct snd_soc_dapm_widget *w,
104 enum snd_soc_dapm_type id,
105 const char *wname, const char *stream)
106{
107 w->id = id;
108 w->name = wname;
109 w->sname = stream;
110 w->reg = SND_SOC_NOPM;
111 w->shift = 0;
112 w->kcontrol_news = NULL;
113 w->num_kcontrols = 0;
114 w->priv = NULL;
115}
116
117static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
118 const char *sink, const char *control, const char *src)
119{
120 route->sink = sink;
121 route->source = src;
122 route->control = control;
123 route->connected = NULL;
124}
125
126static void create_fill_widget_route_map(struct snd_soc_dapm_context *dapm,
127 struct hdac_hdmi_dai_pin_map *dai_map)
128{
129 struct snd_soc_dapm_route route[1];
130 struct snd_soc_dapm_widget widgets[2] = { {0} };
131
132 memset(&route, 0, sizeof(route));
133
134 hdac_hdmi_fill_widget_info(&widgets[0], snd_soc_dapm_output,
135 "hif1 Output", NULL);
136 hdac_hdmi_fill_widget_info(&widgets[1], snd_soc_dapm_aif_in,
137 "Coverter 1", "hif1");
138
139 hdac_hdmi_fill_route(&route[0], "hif1 Output", NULL, "Coverter 1");
140
141 snd_soc_dapm_new_controls(dapm, widgets, ARRAY_SIZE(widgets));
142 snd_soc_dapm_add_routes(dapm, route, ARRAY_SIZE(route));
143}
144
145static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev,
146 struct hdac_hdmi_dai_pin_map *dai_map,
147 hda_nid_t pin_nid, hda_nid_t cvt_nid, int dai_id)
148{
149 int ret;
150
151 dai_map->dai_id = dai_id;
152 dai_map->pin.nid = pin_nid;
153
154 ret = hdac_hdmi_query_pin_connlist(edev, &dai_map->pin);
155 if (ret < 0) {
156 dev_err(&edev->hdac.dev,
157 "Error querying connection list: %d\n", ret);
158 return ret;
159 }
160
161 dai_map->cvt.nid = cvt_nid;
162
163 /* Enable out path for this pin widget */
164 snd_hdac_codec_write(&edev->hdac, pin_nid, 0,
165 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
166
167 /* Enable transmission */
168 snd_hdac_codec_write(&edev->hdac, cvt_nid, 0,
169 AC_VERB_SET_DIGI_CONVERT_1, 1);
170
171 /* Category Code (CC) to zero */
172 snd_hdac_codec_write(&edev->hdac, cvt_nid, 0,
173 AC_VERB_SET_DIGI_CONVERT_2, 0);
174
175 snd_hdac_codec_write(&edev->hdac, pin_nid, 0,
176 AC_VERB_SET_CONNECT_SEL, 0);
177
178 return hdac_hdmi_query_cvt_params(&edev->hdac, &dai_map->cvt);
179}
180
181/*
182 * Parse all nodes and store the cvt/pin nids in array
183 * Add one time initialization for pin and cvt widgets
184 */
185static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
186{
187 hda_nid_t nid;
188 int i;
189 struct hdac_device *hdac = &edev->hdac;
190 struct hdac_hdmi_priv *hdmi = edev->private_data;
191 int cvt_nid = 0, pin_nid = 0;
192
193 hdac->num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid);
194 if (!nid || hdac->num_nodes < 0) {
195 dev_warn(&hdac->dev, "HDMI: failed to get afg sub nodes\n");
196 return -EINVAL;
197 }
198
199 hdac->start_nid = nid;
200
201 for (i = 0; i < hdac->num_nodes; i++, nid++) {
202 unsigned int caps;
203 unsigned int type;
204
205 caps = get_wcaps(hdac, nid);
206 type = get_wcaps_type(caps);
207
208 if (!(caps & AC_WCAP_DIGITAL))
209 continue;
210
211 switch (type) {
212
213 case AC_WID_AUD_OUT:
214 hdmi->cvt_nid[cvt_nid] = nid;
215 cvt_nid++;
216 break;
217
218 case AC_WID_PIN:
219 hdmi->pin_nid[pin_nid] = nid;
220 pin_nid++;
221 break;
222 }
223 }
224
225 hdac->end_nid = nid;
226
227 if (!pin_nid || !cvt_nid)
228 return -EIO;
229
230 /*
231 * Currently on board only 1 pin and 1 converter is enabled for
232 * simplification, more will be added eventually
233 * So using fixed map for dai_id:pin:cvt
234 */
235 return hdac_hdmi_init_dai_map(edev, &hdmi->dai_map[0], hdmi->pin_nid[0],
236 hdmi->cvt_nid[0], 0);
237}
238
239static int hdmi_codec_probe(struct snd_soc_codec *codec)
240{
241 struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
242 struct hdac_hdmi_priv *hdmi = edev->private_data;
243 struct snd_soc_dapm_context *dapm =
244 snd_soc_component_get_dapm(&codec->component);
245
246 edev->scodec = codec;
247
248 create_fill_widget_route_map(dapm, &hdmi->dai_map[0]);
249
250 /* Imp: Store the card pointer in hda_codec */
251 edev->card = dapm->card->snd_card;
252
253 return 0;
254}
255
256static struct snd_soc_codec_driver hdmi_hda_codec = {
257 .probe = hdmi_codec_probe,
258 .idle_bias_off = true,
259};
260
261static struct snd_soc_dai_driver hdmi_dais[] = {
262 { .name = "intel-hdmi-hif1",
263 .playback = {
264 .stream_name = "hif1",
265 .channels_min = 2,
266 .channels_max = 2,
267 .rates = SNDRV_PCM_RATE_32000 |
268 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
269 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
270 SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
271 .formats = SNDRV_PCM_FMTBIT_S16_LE |
272 SNDRV_PCM_FMTBIT_S20_3LE |
273 SNDRV_PCM_FMTBIT_S24_LE |
274 SNDRV_PCM_FMTBIT_S32_LE,
275
276 },
277 },
278};
279
280static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
281{
282 struct hdac_device *codec = &edev->hdac;
283 struct hdac_hdmi_priv *hdmi_priv;
284 int ret = 0;
285
286 hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL);
287 if (hdmi_priv == NULL)
288 return -ENOMEM;
289
290 edev->private_data = hdmi_priv;
291
292 dev_set_drvdata(&codec->dev, edev);
293
294 ret = hdac_hdmi_parse_and_map_nid(edev);
295 if (ret < 0)
296 return ret;
297
298 /* ASoC specific initialization */
299 return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
300 hdmi_dais, ARRAY_SIZE(hdmi_dais));
301}
302
303static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
304{
305 snd_soc_unregister_codec(&edev->hdac.dev);
306
307 return 0;
308}
309
310static const struct hda_device_id hdmi_list[] = {
311 HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0),
312 {}
313};
314
315MODULE_DEVICE_TABLE(hdaudio, hdmi_list);
316
317static struct hdac_ext_driver hdmi_driver = {
318 . hdac = {
319 .driver = {
320 .name = "HDMI HDA Codec",
321 },
322 .id_table = hdmi_list,
323 },
324 .probe = hdac_hdmi_dev_probe,
325 .remove = hdac_hdmi_dev_remove,
326};
327
328static int __init hdmi_init(void)
329{
330 return snd_hda_ext_driver_register(&hdmi_driver);
331}
332
333static void __exit hdmi_exit(void)
334{
335 snd_hda_ext_driver_unregister(&hdmi_driver);
336}
337
338module_init(hdmi_init);
339module_exit(hdmi_exit);
340
341MODULE_LICENSE("GPL v2");
342MODULE_DESCRIPTION("HDMI HD codec");
343MODULE_AUTHOR("Samreen Nilofer<samreen.nilofer@intel.com>");
344MODULE_AUTHOR("Subhransu S. Prusty<subhransu.s.prusty@intel.com>");
This page took 0.037707 seconds and 5 git commands to generate.