brintos

brintos / linux-shallow public Read only

0
0
Text · 4.7 KiB · 423762d Raw
196 lines · c
1// SPDX-License-Identifier: (GPL-2.0+ OR MIT)2/*3 * dw-hdmi-gp-audio.c4 *5 * Copyright 2020-2022 NXP6 */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 speaker44 * 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.150 * Channels	2	4	6	6	6	851 *52 * Our mapping from ALSA channel to CEA686D speaker name and HDMI channel:53 *54 *				Number of ALSA channels55 * ALSA Channel	2	3	4	5	6	7	856 * 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/FRC64 */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	return 0;94}95 96static void audio_shutdown(struct device *dev, void *data)97{98}99 100static int audio_mute_stream(struct device *dev, void *data,101			     bool enable, int direction)102{103	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);104 105	if (!enable)106		dw_hdmi_audio_enable(dw->data.hdmi);107	else108		dw_hdmi_audio_disable(dw->data.hdmi);109 110	return 0;111}112 113static int audio_get_eld(struct device *dev, void *data,114			 u8 *buf, size_t len)115{116	struct dw_hdmi_audio_data *audio = data;117	u8 *eld;118 119	eld = audio->get_eld(audio->hdmi);120	if (eld)121		memcpy(buf, eld, min_t(size_t, MAX_ELD_BYTES, len));122	else123		/* Pass en empty ELD if connector not available */124		memset(buf, 0, len);125 126	return 0;127}128 129static int audio_hook_plugged_cb(struct device *dev, void *data,130				 hdmi_codec_plugged_cb fn,131				 struct device *codec_dev)132{133	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);134 135	return dw_hdmi_set_plugged_cb(dw->data.hdmi, fn, codec_dev);136}137 138static const struct hdmi_codec_ops audio_codec_ops = {139	.hw_params = audio_hw_params,140	.audio_shutdown = audio_shutdown,141	.mute_stream = audio_mute_stream,142	.get_eld = audio_get_eld,143	.hook_plugged_cb = audio_hook_plugged_cb,144};145 146static int snd_dw_hdmi_probe(struct platform_device *pdev)147{148	struct dw_hdmi_audio_data *data = pdev->dev.platform_data;149	struct snd_dw_hdmi *dw;150 151	const struct hdmi_codec_pdata codec_data = {152		.i2s = 1,153		.spdif = 0,154		.ops = &audio_codec_ops,155		.max_i2s_channels = 8,156		.data = data,157	};158 159	dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);160	if (!dw)161		return -ENOMEM;162 163	dw->data = *data;164 165	platform_set_drvdata(pdev, dw);166 167	dw->audio_pdev = platform_device_register_data(&pdev->dev,168						       HDMI_CODEC_DRV_NAME, 1,169						       &codec_data,170						       sizeof(codec_data));171 172	return PTR_ERR_OR_ZERO(dw->audio_pdev);173}174 175static void snd_dw_hdmi_remove(struct platform_device *pdev)176{177	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);178 179	platform_device_unregister(dw->audio_pdev);180}181 182static struct platform_driver snd_dw_hdmi_driver = {183	.probe	= snd_dw_hdmi_probe,184	.remove_new = snd_dw_hdmi_remove,185	.driver	= {186		.name = DRIVER_NAME,187	},188};189 190module_platform_driver(snd_dw_hdmi_driver);191 192MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");193MODULE_DESCRIPTION("Synopsys Designware HDMI GPA ALSA interface");194MODULE_LICENSE("GPL");195MODULE_ALIAS("platform:" DRIVER_NAME);196