aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/patch_hdmi.c
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2015-05-05 08:56:20 -0400
committerTakashi Iwai <tiwai@suse.de>2015-05-18 04:03:01 -0400
commit26e9a960b22413c6a0b8775ff43a6a42c9d30919 (patch)
treededd4793235824ebe828b4ed02aba16d0ed1a2f9 /sound/pci/hda/patch_hdmi.c
parent88871dd25f64b9bf16222e829ad766fac201866a (diff)
ALSA: hda/hdmi - Implement Tegra-specific patch
The HDMI codec on NVIDIA Tegra SoCs has a feature that doesn't exist on the MCP or GPU variants. The highest bit in the vendor-defined scratch registers can be used to trigger an interrupt in the HDMI codec, which is signalled to the HDMI driver. This can be used to pass information, such as the HDA format, to the HDMI driver so that it can reconfigure itself accordingly. While at it, change the name of the codec to Tegra124 since there are no other SoCs in the Tegra12x family. There isn't really a Tegra12x family. Signed-off-by: Thierry Reding <treding@nvidia.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/patch_hdmi.c')
-rw-r--r--sound/pci/hda/patch_hdmi.c167
1 files changed, 166 insertions, 1 deletions
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index fd11ad7fe9f4..fc08946e36bb 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -2930,6 +2930,171 @@ static int patch_nvhdmi(struct hda_codec *codec)
2930} 2930}
2931 2931
2932/* 2932/*
2933 * The HDA codec on NVIDIA Tegra contains two scratch registers that are
2934 * accessed using vendor-defined verbs. These registers can be used for
2935 * interoperability between the HDA and HDMI drivers.
2936 */
2937
2938/* Audio Function Group node */
2939#define NVIDIA_AFG_NID 0x01
2940
2941/*
2942 * The SCRATCH0 register is used to notify the HDMI codec of changes in audio
2943 * format. On Tegra, bit 31 is used as a trigger that causes an interrupt to
2944 * be raised in the HDMI codec. The remainder of the bits is arbitrary. This
2945 * implementation stores the HDA format (see AC_FMT_*) in bits [15:0] and an
2946 * additional bit (at position 30) to signal the validity of the format.
2947 *
2948 * | 31 | 30 | 29 16 | 15 0 |
2949 * +---------+-------+--------+--------+
2950 * | TRIGGER | VALID | UNUSED | FORMAT |
2951 * +-----------------------------------|
2952 *
2953 * Note that for the trigger bit to take effect it needs to change value
2954 * (i.e. it needs to be toggled).
2955 */
2956#define NVIDIA_GET_SCRATCH0 0xfa6
2957#define NVIDIA_SET_SCRATCH0_BYTE0 0xfa7
2958#define NVIDIA_SET_SCRATCH0_BYTE1 0xfa8
2959#define NVIDIA_SET_SCRATCH0_BYTE2 0xfa9
2960#define NVIDIA_SET_SCRATCH0_BYTE3 0xfaa
2961#define NVIDIA_SCRATCH_TRIGGER (1 << 7)
2962#define NVIDIA_SCRATCH_VALID (1 << 6)
2963
2964#define NVIDIA_GET_SCRATCH1 0xfab
2965#define NVIDIA_SET_SCRATCH1_BYTE0 0xfac
2966#define NVIDIA_SET_SCRATCH1_BYTE1 0xfad
2967#define NVIDIA_SET_SCRATCH1_BYTE2 0xfae
2968#define NVIDIA_SET_SCRATCH1_BYTE3 0xfaf
2969
2970/*
2971 * The format parameter is the HDA audio format (see AC_FMT_*). If set to 0,
2972 * the format is invalidated so that the HDMI codec can be disabled.
2973 */
2974static void tegra_hdmi_set_format(struct hda_codec *codec, unsigned int format)
2975{
2976 unsigned int value;
2977
2978 /* bits [31:30] contain the trigger and valid bits */
2979 value = snd_hda_codec_read(codec, NVIDIA_AFG_NID, 0,
2980 NVIDIA_GET_SCRATCH0, 0);
2981 value = (value >> 24) & 0xff;
2982
2983 /* bits [15:0] are used to store the HDA format */
2984 snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0,
2985 NVIDIA_SET_SCRATCH0_BYTE0,
2986 (format >> 0) & 0xff);
2987 snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0,
2988 NVIDIA_SET_SCRATCH0_BYTE1,
2989 (format >> 8) & 0xff);
2990
2991 /* bits [16:24] are unused */
2992 snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0,
2993 NVIDIA_SET_SCRATCH0_BYTE2, 0);
2994
2995 /*
2996 * Bit 30 signals that the data is valid and hence that HDMI audio can
2997 * be enabled.
2998 */
2999 if (format == 0)
3000 value &= ~NVIDIA_SCRATCH_VALID;
3001 else
3002 value |= NVIDIA_SCRATCH_VALID;
3003
3004 /*
3005 * Whenever the trigger bit is toggled, an interrupt is raised in the
3006 * HDMI codec. The HDMI driver will use that as trigger to update its
3007 * configuration.
3008 */
3009 value ^= NVIDIA_SCRATCH_TRIGGER;
3010
3011 snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0,
3012 NVIDIA_SET_SCRATCH0_BYTE3, value);
3013}
3014
3015static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo,
3016 struct hda_codec *codec,
3017 unsigned int stream_tag,
3018 unsigned int format,
3019 struct snd_pcm_substream *substream)
3020{
3021 int err;
3022
3023 err = generic_hdmi_playback_pcm_prepare(hinfo, codec, stream_tag,
3024 format, substream);
3025 if (err < 0)
3026 return err;
3027
3028 /* notify the HDMI codec of the format change */
3029 tegra_hdmi_set_format(codec, format);
3030
3031 return 0;
3032}
3033
3034static int tegra_hdmi_pcm_cleanup(struct hda_pcm_stream *hinfo,
3035 struct hda_codec *codec,
3036 struct snd_pcm_substream *substream)
3037{
3038 /* invalidate the format in the HDMI codec */
3039 tegra_hdmi_set_format(codec, 0);
3040
3041 return generic_hdmi_playback_pcm_cleanup(hinfo, codec, substream);
3042}
3043
3044static struct hda_pcm *hda_find_pcm_by_type(struct hda_codec *codec, int type)
3045{
3046 struct hdmi_spec *spec = codec->spec;
3047 unsigned int i;
3048
3049 for (i = 0; i < spec->num_pins; i++) {
3050 struct hda_pcm *pcm = get_pcm_rec(spec, i);
3051
3052 if (pcm->pcm_type == type)
3053 return pcm;
3054 }
3055
3056 return NULL;
3057}
3058
3059static int tegra_hdmi_build_pcms(struct hda_codec *codec)
3060{
3061 struct hda_pcm_stream *stream;
3062 struct hda_pcm *pcm;
3063 int err;
3064
3065 err = generic_hdmi_build_pcms(codec);
3066 if (err < 0)
3067 return err;
3068
3069 pcm = hda_find_pcm_by_type(codec, HDA_PCM_TYPE_HDMI);
3070 if (!pcm)
3071 return -ENODEV;
3072
3073 /*
3074 * Override ->prepare() and ->cleanup() operations to notify the HDMI
3075 * codec about format changes.
3076 */
3077 stream = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK];
3078 stream->ops.prepare = tegra_hdmi_pcm_prepare;
3079 stream->ops.cleanup = tegra_hdmi_pcm_cleanup;
3080
3081 return 0;
3082}
3083
3084static int patch_tegra_hdmi(struct hda_codec *codec)
3085{
3086 int err;
3087
3088 err = patch_generic_hdmi(codec);
3089 if (err)
3090 return err;
3091
3092 codec->patch_ops.build_pcms = tegra_hdmi_build_pcms;
3093
3094 return 0;
3095}
3096
3097/*
2933 * ATI/AMD-specific implementations 3098 * ATI/AMD-specific implementations
2934 */ 3099 */
2935 3100
@@ -3328,7 +3493,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
3328{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi }, 3493{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi },
3329{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi }, 3494{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi },
3330{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi }, 3495{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi },
3331{ .id = 0x10de0028, .name = "Tegra12x HDMI", .patch = patch_nvhdmi }, 3496{ .id = 0x10de0028, .name = "Tegra124 HDMI", .patch = patch_tegra_hdmi },
3332{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi }, 3497{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi },
3333{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi }, 3498{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi },
3334{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi }, 3499{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi },