blob: 45dfc4f608e5dfa5019b3f8cad5f4ba2a5e636e1 [file] [log] [blame]
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +05301/*
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
Subhransu S. Prustye342ac02015-11-10 18:42:07 +053063static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
64{
65 struct hdac_device *hdac = container_of(dev, struct hdac_device, dev);
66
67 return container_of(hdac, struct hdac_ext_device, hdac);
68}
69
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +053070static int
71hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
72{
73 int err;
74
75 /* Only stereo supported as of now */
76 cvt->params.channels_min = cvt->params.channels_max = 2;
77
78 err = snd_hdac_query_supported_pcm(hdac, cvt->nid,
79 &cvt->params.rates,
80 &cvt->params.formats,
81 &cvt->params.maxbps);
82 if (err < 0)
83 dev_err(&hdac->dev,
84 "Failed to query pcm params for nid %d: %d\n",
85 cvt->nid, err);
86
87 return err;
88}
89
90static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac,
91 struct hdac_hdmi_pin *pin)
92{
93 if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) {
94 dev_warn(&hdac->hdac.dev,
95 "HDMI: pin %d wcaps %#x does not support connection list\n",
96 pin->nid, get_wcaps(&hdac->hdac, pin->nid));
97 return -EINVAL;
98 }
99
100 pin->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid,
101 pin->mux_nids, HDA_MAX_CONNECTIONS);
102 if (pin->num_mux_nids == 0) {
103 dev_err(&hdac->hdac.dev, "No connections found\n");
104 return -ENODEV;
105 }
106
107 return pin->num_mux_nids;
108}
109
110static void hdac_hdmi_fill_widget_info(struct snd_soc_dapm_widget *w,
111 enum snd_soc_dapm_type id,
112 const char *wname, const char *stream)
113{
114 w->id = id;
115 w->name = wname;
116 w->sname = stream;
117 w->reg = SND_SOC_NOPM;
118 w->shift = 0;
119 w->kcontrol_news = NULL;
120 w->num_kcontrols = 0;
121 w->priv = NULL;
122}
123
124static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
125 const char *sink, const char *control, const char *src)
126{
127 route->sink = sink;
128 route->source = src;
129 route->control = control;
130 route->connected = NULL;
131}
132
133static void create_fill_widget_route_map(struct snd_soc_dapm_context *dapm,
134 struct hdac_hdmi_dai_pin_map *dai_map)
135{
136 struct snd_soc_dapm_route route[1];
137 struct snd_soc_dapm_widget widgets[2] = { {0} };
138
139 memset(&route, 0, sizeof(route));
140
141 hdac_hdmi_fill_widget_info(&widgets[0], snd_soc_dapm_output,
142 "hif1 Output", NULL);
143 hdac_hdmi_fill_widget_info(&widgets[1], snd_soc_dapm_aif_in,
144 "Coverter 1", "hif1");
145
146 hdac_hdmi_fill_route(&route[0], "hif1 Output", NULL, "Coverter 1");
147
148 snd_soc_dapm_new_controls(dapm, widgets, ARRAY_SIZE(widgets));
149 snd_soc_dapm_add_routes(dapm, route, ARRAY_SIZE(route));
150}
151
152static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev,
153 struct hdac_hdmi_dai_pin_map *dai_map,
154 hda_nid_t pin_nid, hda_nid_t cvt_nid, int dai_id)
155{
156 int ret;
157
158 dai_map->dai_id = dai_id;
159 dai_map->pin.nid = pin_nid;
160
161 ret = hdac_hdmi_query_pin_connlist(edev, &dai_map->pin);
162 if (ret < 0) {
163 dev_err(&edev->hdac.dev,
164 "Error querying connection list: %d\n", ret);
165 return ret;
166 }
167
168 dai_map->cvt.nid = cvt_nid;
169
170 /* Enable out path for this pin widget */
171 snd_hdac_codec_write(&edev->hdac, pin_nid, 0,
172 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
173
174 /* Enable transmission */
175 snd_hdac_codec_write(&edev->hdac, cvt_nid, 0,
176 AC_VERB_SET_DIGI_CONVERT_1, 1);
177
178 /* Category Code (CC) to zero */
179 snd_hdac_codec_write(&edev->hdac, cvt_nid, 0,
180 AC_VERB_SET_DIGI_CONVERT_2, 0);
181
182 snd_hdac_codec_write(&edev->hdac, pin_nid, 0,
183 AC_VERB_SET_CONNECT_SEL, 0);
184
185 return hdac_hdmi_query_cvt_params(&edev->hdac, &dai_map->cvt);
186}
187
188/*
189 * Parse all nodes and store the cvt/pin nids in array
190 * Add one time initialization for pin and cvt widgets
191 */
192static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
193{
194 hda_nid_t nid;
195 int i;
196 struct hdac_device *hdac = &edev->hdac;
197 struct hdac_hdmi_priv *hdmi = edev->private_data;
198 int cvt_nid = 0, pin_nid = 0;
199
200 hdac->num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid);
201 if (!nid || hdac->num_nodes < 0) {
202 dev_warn(&hdac->dev, "HDMI: failed to get afg sub nodes\n");
203 return -EINVAL;
204 }
205
206 hdac->start_nid = nid;
207
208 for (i = 0; i < hdac->num_nodes; i++, nid++) {
209 unsigned int caps;
210 unsigned int type;
211
212 caps = get_wcaps(hdac, nid);
213 type = get_wcaps_type(caps);
214
215 if (!(caps & AC_WCAP_DIGITAL))
216 continue;
217
218 switch (type) {
219
220 case AC_WID_AUD_OUT:
221 hdmi->cvt_nid[cvt_nid] = nid;
222 cvt_nid++;
223 break;
224
225 case AC_WID_PIN:
226 hdmi->pin_nid[pin_nid] = nid;
227 pin_nid++;
228 break;
229 }
230 }
231
232 hdac->end_nid = nid;
233
234 if (!pin_nid || !cvt_nid)
235 return -EIO;
236
237 /*
238 * Currently on board only 1 pin and 1 converter is enabled for
239 * simplification, more will be added eventually
240 * So using fixed map for dai_id:pin:cvt
241 */
242 return hdac_hdmi_init_dai_map(edev, &hdmi->dai_map[0], hdmi->pin_nid[0],
243 hdmi->cvt_nid[0], 0);
244}
245
246static int hdmi_codec_probe(struct snd_soc_codec *codec)
247{
248 struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
249 struct hdac_hdmi_priv *hdmi = edev->private_data;
250 struct snd_soc_dapm_context *dapm =
251 snd_soc_component_get_dapm(&codec->component);
252
253 edev->scodec = codec;
254
255 create_fill_widget_route_map(dapm, &hdmi->dai_map[0]);
256
257 /* Imp: Store the card pointer in hda_codec */
258 edev->card = dapm->card->snd_card;
259
Subhransu S. Prustye342ac02015-11-10 18:42:07 +0530260 /*
261 * hdac_device core already sets the state to active and calls
262 * get_noresume. So enable runtime and set the device to suspend.
263 */
264 pm_runtime_enable(&edev->hdac.dev);
265 pm_runtime_put(&edev->hdac.dev);
266 pm_runtime_suspend(&edev->hdac.dev);
267
268 return 0;
269}
270
271static int hdmi_codec_remove(struct snd_soc_codec *codec)
272{
273 struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
274
275 pm_runtime_disable(&edev->hdac.dev);
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530276 return 0;
277}
278
279static struct snd_soc_codec_driver hdmi_hda_codec = {
280 .probe = hdmi_codec_probe,
Subhransu S. Prustye342ac02015-11-10 18:42:07 +0530281 .remove = hdmi_codec_remove,
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530282 .idle_bias_off = true,
283};
284
285static struct snd_soc_dai_driver hdmi_dais[] = {
286 { .name = "intel-hdmi-hif1",
287 .playback = {
288 .stream_name = "hif1",
289 .channels_min = 2,
290 .channels_max = 2,
291 .rates = SNDRV_PCM_RATE_32000 |
292 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
293 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
294 SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
295 .formats = SNDRV_PCM_FMTBIT_S16_LE |
296 SNDRV_PCM_FMTBIT_S20_3LE |
297 SNDRV_PCM_FMTBIT_S24_LE |
298 SNDRV_PCM_FMTBIT_S32_LE,
299
300 },
301 },
302};
303
304static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
305{
306 struct hdac_device *codec = &edev->hdac;
307 struct hdac_hdmi_priv *hdmi_priv;
308 int ret = 0;
309
310 hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL);
311 if (hdmi_priv == NULL)
312 return -ENOMEM;
313
314 edev->private_data = hdmi_priv;
315
316 dev_set_drvdata(&codec->dev, edev);
317
318 ret = hdac_hdmi_parse_and_map_nid(edev);
319 if (ret < 0)
320 return ret;
321
322 /* ASoC specific initialization */
323 return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
324 hdmi_dais, ARRAY_SIZE(hdmi_dais));
325}
326
327static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
328{
329 snd_soc_unregister_codec(&edev->hdac.dev);
330
331 return 0;
332}
333
Subhransu S. Prustye342ac02015-11-10 18:42:07 +0530334#ifdef CONFIG_PM
335static int hdac_hdmi_runtime_suspend(struct device *dev)
336{
337 struct hdac_ext_device *edev = to_hda_ext_device(dev);
338 struct hdac_device *hdac = &edev->hdac;
339
340 dev_dbg(dev, "Enter: %s\n", __func__);
341
342 /* Power down afg */
343 if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3))
344 snd_hdac_codec_write(hdac, hdac->afg, 0,
345 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
346
347 return 0;
348}
349
350static int hdac_hdmi_runtime_resume(struct device *dev)
351{
352 struct hdac_ext_device *edev = to_hda_ext_device(dev);
353 struct hdac_device *hdac = &edev->hdac;
354
355 dev_dbg(dev, "Enter: %s\n", __func__);
356
357 /* Power up afg */
358 if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0))
359 snd_hdac_codec_write(hdac, hdac->afg, 0,
360 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
361
362 return 0;
363}
364#else
365#define hdac_hdmi_runtime_suspend NULL
366#define hdac_hdmi_runtime_resume NULL
367#endif
368
369static const struct dev_pm_ops hdac_hdmi_pm = {
370 SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL)
371};
372
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530373static const struct hda_device_id hdmi_list[] = {
374 HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0),
375 {}
376};
377
378MODULE_DEVICE_TABLE(hdaudio, hdmi_list);
379
380static struct hdac_ext_driver hdmi_driver = {
381 . hdac = {
382 .driver = {
383 .name = "HDMI HDA Codec",
Subhransu S. Prustye342ac02015-11-10 18:42:07 +0530384 .pm = &hdac_hdmi_pm,
Subhransu S. Prusty18382ea2015-11-10 18:42:06 +0530385 },
386 .id_table = hdmi_list,
387 },
388 .probe = hdac_hdmi_dev_probe,
389 .remove = hdac_hdmi_dev_remove,
390};
391
392static int __init hdmi_init(void)
393{
394 return snd_hda_ext_driver_register(&hdmi_driver);
395}
396
397static void __exit hdmi_exit(void)
398{
399 snd_hda_ext_driver_unregister(&hdmi_driver);
400}
401
402module_init(hdmi_init);
403module_exit(hdmi_exit);
404
405MODULE_LICENSE("GPL v2");
406MODULE_DESCRIPTION("HDMI HD codec");
407MODULE_AUTHOR("Samreen Nilofer<samreen.nilofer@intel.com>");
408MODULE_AUTHOR("Subhransu S. Prusty<subhransu.s.prusty@intel.com>");