File Index
Files and directories
Sign in
FILE

dw-hdmi-gp-audio.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c C
  1// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
  2/*
  3 * dw-hdmi-gp-audio.c
  4 *
  5 * Copyright 2020-2022 NXP
  6 */
  7#include <linux/io.h>
  8#include <linux/interrupt.h>
  9#include <linux/module.h>
 10#include <linux/platform_device.h>
 11#include <linux/dmaengine.h>
 12#include <linux/dma-mapping.h>
 13#include <drm/bridge/dw_hdmi.h>
 14#include <drm/drm_edid.h>
 15#include <drm/drm_connector.h>
 16
 17#include <sound/hdmi-codec.h>
 18#include <sound/asoundef.h>
 19#include <sound/core.h>
 20#include <sound/initval.h>
 21#include <sound/pcm.h>
 22#include <sound/pcm_drm_eld.h>
 23#include <sound/pcm_iec958.h>
 24#include <sound/dmaengine_pcm.h>
 25
 26#include "dw-hdmi-audio.h"
 27
 28#define DRIVER_NAME "dw-hdmi-gp-audio"
 29#define DRV_NAME    "hdmi-gp-audio"
 30
 31struct snd_dw_hdmi {
 32	struct dw_hdmi_audio_data data;
 33	struct platform_device  *audio_pdev;
 34	unsigned int pos;
 35};
 36
 37struct dw_hdmi_channel_conf {
 38	u8 conf1;
 39	u8 ca;
 40};
 41
 42/*
 43 * The default mapping of ALSA channels to HDMI channels and speaker
 44 * allocation bits.  Note that we can't do channel remapping here -
 45 * channels must be in the same order.
 46 *
 47 * Mappings for alsa-lib pcm/surround*.conf files:
 48 *
 49 *		Front	Sur4.0	Sur4.1	Sur5.0	Sur5.1	Sur7.1
 50 * Channels	2	4	6	6	6	8
 51 *
 52 * Our mapping from ALSA channel to CEA686D speaker name and HDMI channel:
 53 *
 54 *				Number of ALSA channels
 55 * ALSA Channel	2	3	4	5	6	7	8
 56 * 0		FL:0	=	=	=	=	=	=
 57 * 1		FR:1	=	=	=	=	=	=
 58 * 2			FC:3	RL:4	LFE:2	=	=	=
 59 * 3				RR:5	RL:4	FC:3	=	=
 60 * 4					RR:5	RL:4	=	=
 61 * 5						RR:5	=	=
 62 * 6							RC:6	=
 63 * 7							RLC/FRC	RLC/FRC
 64 */
 65static struct dw_hdmi_channel_conf default_hdmi_channel_config[7] = {
 66	{ 0x03, 0x00 },	/* FL,FR */
 67	{ 0x0b, 0x02 },	/* FL,FR,FC */
 68	{ 0x33, 0x08 },	/* FL,FR,RL,RR */
 69	{ 0x37, 0x09 },	/* FL,FR,LFE,RL,RR */
 70	{ 0x3f, 0x0b },	/* FL,FR,LFE,FC,RL,RR */
 71	{ 0x7f, 0x0f },	/* FL,FR,LFE,FC,RL,RR,RC */
 72	{ 0xff, 0x13 },	/* FL,FR,LFE,FC,RL,RR,[FR]RC,[FR]LC */
 73};
 74
 75static int audio_hw_params(struct device *dev,  void *data,
 76			   struct hdmi_codec_daifmt *daifmt,
 77			   struct hdmi_codec_params *params)
 78{
 79	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
 80	u8 ca;
 81
 82	dw_hdmi_set_sample_rate(dw->data.hdmi, params->sample_rate);
 83
 84	ca = default_hdmi_channel_config[params->channels - 2].ca;
 85
 86	dw_hdmi_set_channel_count(dw->data.hdmi, params->channels);
 87	dw_hdmi_set_channel_allocation(dw->data.hdmi, ca);
 88
 89	dw_hdmi_set_sample_non_pcm(dw->data.hdmi,
 90				   params->iec.status[0] & IEC958_AES0_NONAUDIO);
 91	dw_hdmi_set_sample_width(dw->data.hdmi, params->sample_width);
 92
 93	if (daifmt->bit_fmt == SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE)
 94		dw_hdmi_set_sample_iec958(dw->data.hdmi, 1);
 95	else
 96		dw_hdmi_set_sample_iec958(dw->data.hdmi, 0);
 97
 98	return 0;
 99}
100
101static void audio_shutdown(struct device *dev, void *data)
102{
103}
104
105static int audio_mute_stream(struct device *dev, void *data,
106			     bool enable, int direction)
107{
108	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
109
110	if (!enable)
111		dw_hdmi_audio_enable(dw->data.hdmi);
112	else
113		dw_hdmi_audio_disable(dw->data.hdmi);
114
115	return 0;
116}
117
118static int audio_get_eld(struct device *dev, void *data,
119			 u8 *buf, size_t len)
120{
121	struct dw_hdmi_audio_data *audio = data;
122	u8 *eld;
123
124	eld = audio->get_eld(audio->hdmi);
125	if (eld)
126		memcpy(buf, eld, min_t(size_t, MAX_ELD_BYTES, len));
127	else
128		/* Pass en empty ELD if connector not available */
129		memset(buf, 0, len);
130
131	return 0;
132}
133
134static int audio_hook_plugged_cb(struct device *dev, void *data,
135				 hdmi_codec_plugged_cb fn,
136				 struct device *codec_dev)
137{
138	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
139
140	return dw_hdmi_set_plugged_cb(dw->data.hdmi, fn, codec_dev);
141}
142
143static const struct hdmi_codec_ops audio_codec_ops = {
144	.hw_params = audio_hw_params,
145	.audio_shutdown = audio_shutdown,
146	.mute_stream = audio_mute_stream,
147	.get_eld = audio_get_eld,
148	.hook_plugged_cb = audio_hook_plugged_cb,
149};
150
151static int snd_dw_hdmi_probe(struct platform_device *pdev)
152{
153	struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
154	struct snd_dw_hdmi *dw;
155
156	const struct hdmi_codec_pdata codec_data = {
157		.i2s = 1,
158		.spdif = 0,
159		.ops = &audio_codec_ops,
160		.max_i2s_channels = 8,
161		.data = data,
162	};
163
164	dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);
165	if (!dw)
166		return -ENOMEM;
167
168	dw->data = *data;
169
170	platform_set_drvdata(pdev, dw);
171
172	dw->audio_pdev = platform_device_register_data(&pdev->dev,
173						       HDMI_CODEC_DRV_NAME, 1,
174						       &codec_data,
175						       sizeof(codec_data));
176
177	return PTR_ERR_OR_ZERO(dw->audio_pdev);
178}
179
180static void snd_dw_hdmi_remove(struct platform_device *pdev)
181{
182	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
183
184	platform_device_unregister(dw->audio_pdev);
185}
186
187static struct platform_driver snd_dw_hdmi_driver = {
188	.probe	= snd_dw_hdmi_probe,
189	.remove = snd_dw_hdmi_remove,
190	.driver	= {
191		.name = DRIVER_NAME,
192	},
193};
194
195module_platform_driver(snd_dw_hdmi_driver);
196
197MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
198MODULE_DESCRIPTION("Synopsys Designware HDMI GPA ALSA interface");
199MODULE_LICENSE("GPL");
200MODULE_ALIAS("platform:" DRIVER_NAME);
~/ Read-only file preview.