1/* 2 * Universal Interface for Intel High Definition Audio Codec 3 * 4 * HD audio interface patch for ATI HDMI codecs 5 * 6 * Copyright (c) 2006 ATI Technologies Inc. 7 * 8 * 9 * This driver 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; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This driver is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 */ 23 24#include <linux/init.h> 25#include <linux/delay.h> 26#include <linux/slab.h> 27#include <sound/core.h> 28#include "hda_codec.h" 29#include "hda_local.h" 30 31struct atihdmi_spec { 32 struct hda_multi_out multiout; 33 34 struct hda_pcm pcm_rec; 35}; 36 37#define CVT_NID 0x02 /* audio converter */ 38#define PIN_NID 0x03 /* HDMI output pin */ 39 40static struct hda_verb atihdmi_basic_init[] = { 41 /* enable digital output on pin widget */ 42 { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, 43 {} /* terminator */ 44}; 45 46/* 47 * Controls 48 */ 49static int atihdmi_build_controls(struct hda_codec *codec) 50{ 51 struct atihdmi_spec *spec = codec->spec; 52 int err; 53 54 err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); 55 if (err < 0) 56 return err; 57 58 return 0; 59} 60 61static int atihdmi_init(struct hda_codec *codec) 62{ 63 snd_hda_sequence_write(codec, atihdmi_basic_init); 64 /* SI codec requires to unmute the pin */ 65 if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP) 66 snd_hda_codec_write(codec, PIN_NID, 0, 67 AC_VERB_SET_AMP_GAIN_MUTE, 68 AMP_OUT_UNMUTE); 69 return 0; 70} 71 72/* 73 * Digital out 74 */ 75static int atihdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 76 struct hda_codec *codec, 77 struct snd_pcm_substream *substream) 78{ 79 struct atihdmi_spec *spec = codec->spec; 80 return snd_hda_multi_out_dig_open(codec, &spec->multiout); 81} 82 83static int atihdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 84 struct hda_codec *codec, 85 struct snd_pcm_substream *substream) 86{ 87 struct atihdmi_spec *spec = codec->spec; 88 return snd_hda_multi_out_dig_close(codec, &spec->multiout); 89} 90 91static int atihdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 92 struct hda_codec *codec, 93 unsigned int stream_tag, 94 unsigned int format, 95 struct snd_pcm_substream *substream) 96{ 97 struct atihdmi_spec *spec = codec->spec; 98 int chans = substream->runtime->channels; 99 int i, err; 100 101 err = snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, 102 format, substream); 103 if (err < 0) 104 return err; 105 snd_hda_codec_write(codec, CVT_NID, 0, AC_VERB_SET_CVT_CHAN_COUNT, 106 chans - 1); 107 for (i = 0; i < chans; i++) { 108 snd_hda_codec_write(codec, CVT_NID, 0, 109 AC_VERB_SET_HDMI_CHAN_SLOT, 110 (i << 4) | i); 111 } 112 return 0; 113} 114 115static struct hda_pcm_stream atihdmi_pcm_digital_playback = { 116 .substreams = 1, 117 .channels_min = 2, 118 .channels_max = 2, 119 .nid = CVT_NID, /* NID to query formats and rates and setup streams */ 120 .ops = { 121 .open = atihdmi_dig_playback_pcm_open, 122 .close = atihdmi_dig_playback_pcm_close, 123 .prepare = atihdmi_dig_playback_pcm_prepare 124 }, 125}; 126 127static int atihdmi_build_pcms(struct hda_codec *codec) 128{ 129 struct atihdmi_spec *spec = codec->spec; 130 struct hda_pcm *info = &spec->pcm_rec; 131 unsigned int chans; 132 133 codec->num_pcms = 1; 134 codec->pcm_info = info; 135 136 info->name = "ATI HDMI"; 137 info->pcm_type = HDA_PCM_TYPE_HDMI; 138 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback; 139 140 chans = get_wcaps(codec, CVT_NID); 141 chans = get_wcaps_channels(chans); 142 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans; 143 144 return 0; 145} 146 147static void atihdmi_free(struct hda_codec *codec) 148{ 149 kfree(codec->spec); 150} 151 152static struct hda_codec_ops atihdmi_patch_ops = { 153 .build_controls = atihdmi_build_controls, 154 .build_pcms = atihdmi_build_pcms, 155 .init = atihdmi_init, 156 .free = atihdmi_free, 157}; 158 159static int patch_atihdmi(struct hda_codec *codec) 160{ 161 struct atihdmi_spec *spec; 162 163 spec = kzalloc(sizeof(*spec), GFP_KERNEL); 164 if (spec == NULL) 165 return -ENOMEM; 166 167 codec->spec = spec; 168 169 spec->multiout.num_dacs = 0; /* no analog */ 170 spec->multiout.max_channels = 2; 171 /* NID for copying analog to digital, 172 * seems to be unused in pure-digital 173 * case. 174 */ 175 spec->multiout.dig_out_nid = CVT_NID; 176 177 codec->patch_ops = atihdmi_patch_ops; 178 179 return 0; 180} 181 182/* 183 * patch entries 184 */ 185static struct hda_codec_preset snd_hda_preset_atihdmi[] = { 186 { .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi }, 187 { .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi }, 188 { .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi }, 189 { .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi }, 190 { .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi }, 191 { .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_atihdmi }, 192 {} /* terminator */ 193}; 194 195MODULE_ALIAS("snd-hda-codec-id:1002793c"); 196MODULE_ALIAS("snd-hda-codec-id:10027919"); 197MODULE_ALIAS("snd-hda-codec-id:1002791a"); 198MODULE_ALIAS("snd-hda-codec-id:1002aa01"); 199MODULE_ALIAS("snd-hda-codec-id:10951390"); 200MODULE_ALIAS("snd-hda-codec-id:17e80047"); 201 202MODULE_LICENSE("GPL"); 203MODULE_DESCRIPTION("ATI HDMI HD-audio codec"); 204 205static struct hda_codec_preset_list atihdmi_list = { 206 .preset = snd_hda_preset_atihdmi, 207 .owner = THIS_MODULE, 208}; 209 210static int __init patch_atihdmi_init(void) 211{ 212 return snd_hda_add_codec_preset(&atihdmi_list); 213} 214 215static void __exit patch_atihdmi_exit(void) 216{ 217 snd_hda_delete_codec_preset(&atihdmi_list); 218} 219 220module_init(patch_atihdmi_init) 221module_exit(patch_atihdmi_exit) 222