diff options
Diffstat (limited to 'sound/pci/hda/patch_nvhdmi.c')
-rw-r--r-- | sound/pci/hda/patch_nvhdmi.c | 279 |
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 | |||
46 | static hda_nid_t nvhdmi_convert_nids[4] = { | ||
47 | /*front, rear, clfe, rear_surr */ | ||
48 | 0x6, 0x8, 0xa, 0xc, | ||
49 | }; | ||
50 | |||
38 | static struct hda_verb nvhdmi_basic_init[] = { | 51 | static 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 | */ |
68 | static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, | 87 | static 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 | ||
76 | static int nvhdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, | 95 | static 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 | ||
84 | static int nvhdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, | 116 | static 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 | |||
124 | static 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 | |||
245 | static 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 | ||
95 | static struct hda_pcm_stream nvhdmi_pcm_digital_playback = { | 256 | static 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 | |||
271 | static 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 | ||
110 | static int nvhdmi_build_pcms(struct hda_codec *codec) | 286 | static 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 | |||
302 | static 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 | ||
130 | static struct hda_codec_ops nvhdmi_patch_ops = { | 323 | static 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 | |||
330 | static 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 | ||
137 | static int patch_nvhdmi(struct hda_codec *codec) | 337 | static 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 | |||
356 | static 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 | */ |
161 | static struct hda_codec_preset snd_hda_preset_nvhdmi[] = { | 378 | static 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 | ||