aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorWei Ni <wni@nvidia.com>2009-06-01 04:37:28 -0400
committerTakashi Iwai <tiwai@suse.de>2009-06-01 05:05:26 -0400
commita3d6ab9723060e8d67e28a8d9142e6d08953d07b (patch)
tree16437fdf8a2a0e593db3c91b682b33a74e4627df /sound
parent8a933ece41a59ce077eeffe5b9bf08b14d173c58 (diff)
ALSA: hda - Support NVIDIA 8 channel HDMI audio
Support 8 channel HDMI audio for MCP78/7A Signed-off-by: Wei Ni <wni@nvidia.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/pci/hda/patch_nvhdmi.c279
1 files changed, 248 insertions, 31 deletions
diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c
index d57d8132a06e..f5792e2eea82 100644
--- a/sound/pci/hda/patch_nvhdmi.c
+++ b/sound/pci/hda/patch_nvhdmi.c
@@ -35,9 +35,28 @@ struct nvhdmi_spec {
35 struct hda_pcm pcm_rec; 35 struct hda_pcm pcm_rec;
36}; 36};
37 37
38#define Nv_VERB_SET_Channel_Allocation 0xF79
39#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A
40#define Nv_VERB_SET_Audio_Protection_On 0xF98
41#define Nv_VERB_SET_Audio_Protection_Off 0xF99
42
43#define Nv_Master_Convert_nid 0x04
44#define Nv_Master_Pin_nid 0x05
45
46static hda_nid_t nvhdmi_convert_nids[4] = {
47 /*front, rear, clfe, rear_surr */
48 0x6, 0x8, 0xa, 0xc,
49};
50
38static struct hda_verb nvhdmi_basic_init[] = { 51static struct hda_verb nvhdmi_basic_init[] = {
52 /* set audio protect on */
53 { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
39 /* enable digital output on pin widget */ 54 /* enable digital output on pin widget */
40 { 0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, 55 { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
56 { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
57 { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
58 { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
59 { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
41 {} /* terminator */ 60 {} /* terminator */
42}; 61};
43 62
@@ -66,48 +85,205 @@ static int nvhdmi_init(struct hda_codec *codec)
66 * Digital out 85 * Digital out
67 */ 86 */
68static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 87static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
69 struct hda_codec *codec, 88 struct hda_codec *codec,
70 struct snd_pcm_substream *substream) 89 struct snd_pcm_substream *substream)
71{ 90{
72 struct nvhdmi_spec *spec = codec->spec; 91 struct nvhdmi_spec *spec = codec->spec;
73 return snd_hda_multi_out_dig_open(codec, &spec->multiout); 92 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
74} 93}
75 94
76static int nvhdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 95static int nvhdmi_dig_playback_pcm_close_8ch(struct hda_pcm_stream *hinfo,
77 struct hda_codec *codec, 96 struct hda_codec *codec,
78 struct snd_pcm_substream *substream) 97 struct snd_pcm_substream *substream)
79{ 98{
80 struct nvhdmi_spec *spec = codec->spec; 99 struct nvhdmi_spec *spec = codec->spec;
100 int i;
101
102 snd_hda_codec_write(codec, Nv_Master_Convert_nid,
103 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
104 for (i = 0; i < 4; i++) {
105 /* set the stream id */
106 snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0,
107 AC_VERB_SET_CHANNEL_STREAMID, 0);
108 /* set the stream format */
109 snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0,
110 AC_VERB_SET_STREAM_FORMAT, 0);
111 }
112
81 return snd_hda_multi_out_dig_close(codec, &spec->multiout); 113 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
82} 114}
83 115
84static int nvhdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 116static int nvhdmi_dig_playback_pcm_close_2ch(struct hda_pcm_stream *hinfo,
85 struct hda_codec *codec, 117 struct hda_codec *codec,
86 unsigned int stream_tag, 118 struct snd_pcm_substream *substream)
87 unsigned int format, 119{
88 struct snd_pcm_substream *substream) 120 struct nvhdmi_spec *spec = codec->spec;
121 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
122}
123
124static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
125 struct hda_codec *codec,
126 unsigned int stream_tag,
127 unsigned int format,
128 struct snd_pcm_substream *substream)
129{
130 int chs;
131 unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
132 int i;
133
134 mutex_lock(&codec->spdif_mutex);
135
136 chs = substream->runtime->channels;
137 chan = chs ? (chs - 1) : 1;
138
139 switch (chs) {
140 default:
141 case 0:
142 case 2:
143 chanmask = 0x00;
144 break;
145 case 4:
146 chanmask = 0x08;
147 break;
148 case 6:
149 chanmask = 0x0b;
150 break;
151 case 8:
152 chanmask = 0x13;
153 break;
154 }
155 dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
156 dataDCC2 = 0x2;
157
158 /* set the Audio InforFrame Channel Allocation */
159 snd_hda_codec_write(codec, 0x1, 0,
160 Nv_VERB_SET_Channel_Allocation, chanmask);
161
162 /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
163 if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
164 snd_hda_codec_write(codec,
165 Nv_Master_Convert_nid,
166 0,
167 AC_VERB_SET_DIGI_CONVERT_1,
168 codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
169
170 /* set the stream id */
171 snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0,
172 AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
173
174 /* set the stream format */
175 snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0,
176 AC_VERB_SET_STREAM_FORMAT, format);
177
178 /* turn on again (if needed) */
179 /* enable and set the channel status audio/data flag */
180 if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
181 snd_hda_codec_write(codec,
182 Nv_Master_Convert_nid,
183 0,
184 AC_VERB_SET_DIGI_CONVERT_1,
185 codec->spdif_ctls & 0xff);
186 snd_hda_codec_write(codec,
187 Nv_Master_Convert_nid,
188 0,
189 AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
190 }
191
192 for (i = 0; i < 4; i++) {
193 if (chs == 2)
194 channel_id = 0;
195 else
196 channel_id = i * 2;
197
198 /* turn off SPDIF once;
199 *otherwise the IEC958 bits won't be updated
200 */
201 if (codec->spdif_status_reset &&
202 (codec->spdif_ctls & AC_DIG1_ENABLE))
203 snd_hda_codec_write(codec,
204 nvhdmi_convert_nids[i],
205 0,
206 AC_VERB_SET_DIGI_CONVERT_1,
207 codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
208 /* set the stream id */
209 snd_hda_codec_write(codec,
210 nvhdmi_convert_nids[i],
211 0,
212 AC_VERB_SET_CHANNEL_STREAMID,
213 (stream_tag << 4) | channel_id);
214 /* set the stream format */
215 snd_hda_codec_write(codec,
216 nvhdmi_convert_nids[i],
217 0,
218 AC_VERB_SET_STREAM_FORMAT,
219 format);
220 /* turn on again (if needed) */
221 /* enable and set the channel status audio/data flag */
222 if (codec->spdif_status_reset &&
223 (codec->spdif_ctls & AC_DIG1_ENABLE)) {
224 snd_hda_codec_write(codec,
225 nvhdmi_convert_nids[i],
226 0,
227 AC_VERB_SET_DIGI_CONVERT_1,
228 codec->spdif_ctls & 0xff);
229 snd_hda_codec_write(codec,
230 nvhdmi_convert_nids[i],
231 0,
232 AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
233 }
234 }
235
236 /* set the Audio Info Frame Checksum */
237 snd_hda_codec_write(codec, 0x1, 0,
238 Nv_VERB_SET_Info_Frame_Checksum,
239 (0x71 - chan - chanmask));
240
241 mutex_unlock(&codec->spdif_mutex);
242 return 0;
243}
244
245static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo,
246 struct hda_codec *codec,
247 unsigned int stream_tag,
248 unsigned int format,
249 struct snd_pcm_substream *substream)
89{ 250{
90 struct nvhdmi_spec *spec = codec->spec; 251 struct nvhdmi_spec *spec = codec->spec;
91 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, 252 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
92 format, substream); 253 format, substream);
93} 254}
94 255
95static struct hda_pcm_stream nvhdmi_pcm_digital_playback = { 256static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch = {
257 .substreams = 1,
258 .channels_min = 2,
259 .channels_max = 8,
260 .nid = Nv_Master_Convert_nid,
261 .rates = SNDRV_PCM_RATE_48000,
262 .maxbps = 16,
263 .formats = SNDRV_PCM_FMTBIT_S16_LE,
264 .ops = {
265 .open = nvhdmi_dig_playback_pcm_open,
266 .close = nvhdmi_dig_playback_pcm_close_8ch,
267 .prepare = nvhdmi_dig_playback_pcm_prepare_8ch
268 },
269};
270
271static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = {
96 .substreams = 1, 272 .substreams = 1,
97 .channels_min = 2, 273 .channels_min = 2,
98 .channels_max = 2, 274 .channels_max = 2,
99 .nid = 0x4, /* NID to query formats and rates and setup streams */ 275 .nid = Nv_Master_Convert_nid,
100 .rates = SNDRV_PCM_RATE_48000, 276 .rates = SNDRV_PCM_RATE_48000,
101 .maxbps = 16, 277 .maxbps = 16,
102 .formats = SNDRV_PCM_FMTBIT_S16_LE, 278 .formats = SNDRV_PCM_FMTBIT_S16_LE,
103 .ops = { 279 .ops = {
104 .open = nvhdmi_dig_playback_pcm_open, 280 .open = nvhdmi_dig_playback_pcm_open,
105 .close = nvhdmi_dig_playback_pcm_close, 281 .close = nvhdmi_dig_playback_pcm_close_2ch,
106 .prepare = nvhdmi_dig_playback_pcm_prepare 282 .prepare = nvhdmi_dig_playback_pcm_prepare_2ch
107 }, 283 },
108}; 284};
109 285
110static int nvhdmi_build_pcms(struct hda_codec *codec) 286static int nvhdmi_build_pcms_8ch(struct hda_codec *codec)
111{ 287{
112 struct nvhdmi_spec *spec = codec->spec; 288 struct nvhdmi_spec *spec = codec->spec;
113 struct hda_pcm *info = &spec->pcm_rec; 289 struct hda_pcm *info = &spec->pcm_rec;
@@ -117,7 +293,24 @@ static int nvhdmi_build_pcms(struct hda_codec *codec)
117 293
118 info->name = "NVIDIA HDMI"; 294 info->name = "NVIDIA HDMI";
119 info->pcm_type = HDA_PCM_TYPE_HDMI; 295 info->pcm_type = HDA_PCM_TYPE_HDMI;
120 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = nvhdmi_pcm_digital_playback; 296 info->stream[SNDRV_PCM_STREAM_PLAYBACK]
297 = nvhdmi_pcm_digital_playback_8ch;
298
299 return 0;
300}
301
302static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
303{
304 struct nvhdmi_spec *spec = codec->spec;
305 struct hda_pcm *info = &spec->pcm_rec;
306
307 codec->num_pcms = 1;
308 codec->pcm_info = info;
309
310 info->name = "NVIDIA HDMI";
311 info->pcm_type = HDA_PCM_TYPE_HDMI;
312 info->stream[SNDRV_PCM_STREAM_PLAYBACK]
313 = nvhdmi_pcm_digital_playback_2ch;
121 314
122 return 0; 315 return 0;
123} 316}
@@ -127,14 +320,40 @@ static void nvhdmi_free(struct hda_codec *codec)
127 kfree(codec->spec); 320 kfree(codec->spec);
128} 321}
129 322
130static struct hda_codec_ops nvhdmi_patch_ops = { 323static struct hda_codec_ops nvhdmi_patch_ops_8ch = {
324 .build_controls = nvhdmi_build_controls,
325 .build_pcms = nvhdmi_build_pcms_8ch,
326 .init = nvhdmi_init,
327 .free = nvhdmi_free,
328};
329
330static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
131 .build_controls = nvhdmi_build_controls, 331 .build_controls = nvhdmi_build_controls,
132 .build_pcms = nvhdmi_build_pcms, 332 .build_pcms = nvhdmi_build_pcms_2ch,
133 .init = nvhdmi_init, 333 .init = nvhdmi_init,
134 .free = nvhdmi_free, 334 .free = nvhdmi_free,
135}; 335};
136 336
137static int patch_nvhdmi(struct hda_codec *codec) 337static int patch_nvhdmi_8ch(struct hda_codec *codec)
338{
339 struct nvhdmi_spec *spec;
340
341 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
342 if (spec == NULL)
343 return -ENOMEM;
344
345 codec->spec = spec;
346
347 spec->multiout.num_dacs = 0; /* no analog */
348 spec->multiout.max_channels = 8;
349 spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
350
351 codec->patch_ops = nvhdmi_patch_ops_8ch;
352
353 return 0;
354}
355
356static int patch_nvhdmi_2ch(struct hda_codec *codec)
138{ 357{
139 struct nvhdmi_spec *spec; 358 struct nvhdmi_spec *spec;
140 359
@@ -144,13 +363,11 @@ static int patch_nvhdmi(struct hda_codec *codec)
144 363
145 codec->spec = spec; 364 codec->spec = spec;
146 365
147 spec->multiout.num_dacs = 0; /* no analog */ 366 spec->multiout.num_dacs = 0; /* no analog */
148 spec->multiout.max_channels = 2; 367 spec->multiout.max_channels = 2;
149 spec->multiout.dig_out_nid = 0x4; /* NID for copying analog to digital, 368 spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
150 * seems to be unused in pure-digital
151 * case. */
152 369
153 codec->patch_ops = nvhdmi_patch_ops; 370 codec->patch_ops = nvhdmi_patch_ops_2ch;
154 371
155 return 0; 372 return 0;
156} 373}
@@ -159,11 +376,11 @@ static int patch_nvhdmi(struct hda_codec *codec)
159 * patch entries 376 * patch entries
160 */ 377 */
161static struct hda_codec_preset snd_hda_preset_nvhdmi[] = { 378static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
162 { .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi }, 379 { .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
163 { .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi }, 380 { .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
164 { .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi }, 381 { .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch },
165 { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi }, 382 { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
166 { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi }, 383 { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
167 {} /* terminator */ 384 {} /* terminator */
168}; 385};
169 386