linux/sound/pci/hda/patch_atihdmi.c
<<
>>
Prefs
   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        /* FIXME: XXX */
 108        for (i = 0; i < chans; i++) {
 109                snd_hda_codec_write(codec, CVT_NID, 0,
 110                                    AC_VERB_SET_HDMI_CHAN_SLOT,
 111                                    (i << 4) | i);
 112        }
 113        return 0;
 114}
 115
 116static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
 117        .substreams = 1,
 118        .channels_min = 2,
 119        .channels_max = 2,
 120        .nid = CVT_NID, /* NID to query formats and rates and setup streams */
 121        .ops = {
 122                .open = atihdmi_dig_playback_pcm_open,
 123                .close = atihdmi_dig_playback_pcm_close,
 124                .prepare = atihdmi_dig_playback_pcm_prepare
 125        },
 126};
 127
 128static int atihdmi_build_pcms(struct hda_codec *codec)
 129{
 130        struct atihdmi_spec *spec = codec->spec;
 131        struct hda_pcm *info = &spec->pcm_rec;
 132        unsigned int chans;
 133
 134        codec->num_pcms = 1;
 135        codec->pcm_info = info;
 136
 137        info->name = "ATI HDMI";
 138        info->pcm_type = HDA_PCM_TYPE_HDMI;
 139        info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback;
 140
 141        /* FIXME: we must check ELD and change the PCM parameters dynamically
 142         */
 143        chans = get_wcaps(codec, CVT_NID);
 144        chans = get_wcaps_channels(chans);
 145        info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
 146
 147        return 0;
 148}
 149
 150static void atihdmi_free(struct hda_codec *codec)
 151{
 152        kfree(codec->spec);
 153}
 154
 155static struct hda_codec_ops atihdmi_patch_ops = {
 156        .build_controls = atihdmi_build_controls,
 157        .build_pcms = atihdmi_build_pcms,
 158        .init = atihdmi_init,
 159        .free = atihdmi_free,
 160};
 161
 162static int patch_atihdmi(struct hda_codec *codec)
 163{
 164        struct atihdmi_spec *spec;
 165
 166        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 167        if (spec == NULL)
 168                return -ENOMEM;
 169
 170        codec->spec = spec;
 171
 172        spec->multiout.num_dacs = 0;      /* no analog */
 173        spec->multiout.max_channels = 2;
 174        /* NID for copying analog to digital,
 175         * seems to be unused in pure-digital
 176         * case.
 177         */
 178        spec->multiout.dig_out_nid = CVT_NID;
 179
 180        codec->patch_ops = atihdmi_patch_ops;
 181
 182        return 0;
 183}
 184
 185/*
 186 * patch entries
 187 */
 188static struct hda_codec_preset snd_hda_preset_atihdmi[] = {
 189        { .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
 190        { .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
 191        { .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
 192        { .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
 193        { .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi },
 194        { .id = 0x17e80047, .name = "Chrontel HDMI",  .patch = patch_atihdmi },
 195        {} /* terminator */
 196};
 197
 198MODULE_ALIAS("snd-hda-codec-id:1002793c");
 199MODULE_ALIAS("snd-hda-codec-id:10027919");
 200MODULE_ALIAS("snd-hda-codec-id:1002791a");
 201MODULE_ALIAS("snd-hda-codec-id:1002aa01");
 202MODULE_ALIAS("snd-hda-codec-id:10951390");
 203MODULE_ALIAS("snd-hda-codec-id:17e80047");
 204
 205MODULE_LICENSE("GPL");
 206MODULE_DESCRIPTION("ATI HDMI HD-audio codec");
 207
 208static struct hda_codec_preset_list atihdmi_list = {
 209        .preset = snd_hda_preset_atihdmi,
 210        .owner = THIS_MODULE,
 211};
 212
 213static int __init patch_atihdmi_init(void)
 214{
 215        return snd_hda_add_codec_preset(&atihdmi_list);
 216}
 217
 218static void __exit patch_atihdmi_exit(void)
 219{
 220        snd_hda_delete_codec_preset(&atihdmi_list);
 221}
 222
 223module_init(patch_atihdmi_init)
 224module_exit(patch_atihdmi_exit)
 225