123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
- /*
- * dw-hdmi-gp-audio.c
- *
- * Copyright 2020-2022 NXP
- */
- #include <linux/io.h>
- #include <linux/interrupt.h>
- #include <linux/module.h>
- #include <linux/platform_device.h>
- #include <linux/dmaengine.h>
- #include <linux/dma-mapping.h>
- #include <drm/bridge/dw_hdmi.h>
- #include <drm/drm_edid.h>
- #include <drm/drm_connector.h>
- #include <sound/hdmi-codec.h>
- #include <sound/asoundef.h>
- #include <sound/core.h>
- #include <sound/initval.h>
- #include <sound/pcm.h>
- #include <sound/pcm_drm_eld.h>
- #include <sound/pcm_iec958.h>
- #include <sound/dmaengine_pcm.h>
- #include "dw-hdmi-audio.h"
- #define DRIVER_NAME "dw-hdmi-gp-audio"
- #define DRV_NAME "hdmi-gp-audio"
- struct snd_dw_hdmi {
- struct dw_hdmi_audio_data data;
- struct platform_device *audio_pdev;
- unsigned int pos;
- };
- struct dw_hdmi_channel_conf {
- u8 conf1;
- u8 ca;
- };
- /*
- * The default mapping of ALSA channels to HDMI channels and speaker
- * allocation bits. Note that we can't do channel remapping here -
- * channels must be in the same order.
- *
- * Mappings for alsa-lib pcm/surround*.conf files:
- *
- * Front Sur4.0 Sur4.1 Sur5.0 Sur5.1 Sur7.1
- * Channels 2 4 6 6 6 8
- *
- * Our mapping from ALSA channel to CEA686D speaker name and HDMI channel:
- *
- * Number of ALSA channels
- * ALSA Channel 2 3 4 5 6 7 8
- * 0 FL:0 = = = = = =
- * 1 FR:1 = = = = = =
- * 2 FC:3 RL:4 LFE:2 = = =
- * 3 RR:5 RL:4 FC:3 = =
- * 4 RR:5 RL:4 = =
- * 5 RR:5 = =
- * 6 RC:6 =
- * 7 RLC/FRC RLC/FRC
- */
- static struct dw_hdmi_channel_conf default_hdmi_channel_config[7] = {
- { 0x03, 0x00 }, /* FL,FR */
- { 0x0b, 0x02 }, /* FL,FR,FC */
- { 0x33, 0x08 }, /* FL,FR,RL,RR */
- { 0x37, 0x09 }, /* FL,FR,LFE,RL,RR */
- { 0x3f, 0x0b }, /* FL,FR,LFE,FC,RL,RR */
- { 0x7f, 0x0f }, /* FL,FR,LFE,FC,RL,RR,RC */
- { 0xff, 0x13 }, /* FL,FR,LFE,FC,RL,RR,[FR]RC,[FR]LC */
- };
- static int audio_hw_params(struct device *dev, void *data,
- struct hdmi_codec_daifmt *daifmt,
- struct hdmi_codec_params *params)
- {
- struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
- u8 ca;
- dw_hdmi_set_sample_rate(dw->data.hdmi, params->sample_rate);
- ca = default_hdmi_channel_config[params->channels - 2].ca;
- dw_hdmi_set_channel_count(dw->data.hdmi, params->channels);
- dw_hdmi_set_channel_allocation(dw->data.hdmi, ca);
- dw_hdmi_set_sample_non_pcm(dw->data.hdmi,
- params->iec.status[0] & IEC958_AES0_NONAUDIO);
- dw_hdmi_set_sample_width(dw->data.hdmi, params->sample_width);
- return 0;
- }
- static void audio_shutdown(struct device *dev, void *data)
- {
- }
- static int audio_mute_stream(struct device *dev, void *data,
- bool enable, int direction)
- {
- struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
- if (!enable)
- dw_hdmi_audio_enable(dw->data.hdmi);
- else
- dw_hdmi_audio_disable(dw->data.hdmi);
- return 0;
- }
- static int audio_get_eld(struct device *dev, void *data,
- u8 *buf, size_t len)
- {
- struct dw_hdmi_audio_data *audio = data;
- u8 *eld;
- eld = audio->get_eld(audio->hdmi);
- if (eld)
- memcpy(buf, eld, min_t(size_t, MAX_ELD_BYTES, len));
- else
- /* Pass en empty ELD if connector not available */
- memset(buf, 0, len);
- return 0;
- }
- static int audio_hook_plugged_cb(struct device *dev, void *data,
- hdmi_codec_plugged_cb fn,
- struct device *codec_dev)
- {
- struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
- return dw_hdmi_set_plugged_cb(dw->data.hdmi, fn, codec_dev);
- }
- static const struct hdmi_codec_ops audio_codec_ops = {
- .hw_params = audio_hw_params,
- .audio_shutdown = audio_shutdown,
- .mute_stream = audio_mute_stream,
- .get_eld = audio_get_eld,
- .hook_plugged_cb = audio_hook_plugged_cb,
- };
- static int snd_dw_hdmi_probe(struct platform_device *pdev)
- {
- struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
- struct snd_dw_hdmi *dw;
- const struct hdmi_codec_pdata codec_data = {
- .i2s = 1,
- .spdif = 0,
- .ops = &audio_codec_ops,
- .max_i2s_channels = 8,
- .data = data,
- };
- dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);
- if (!dw)
- return -ENOMEM;
- dw->data = *data;
- platform_set_drvdata(pdev, dw);
- dw->audio_pdev = platform_device_register_data(&pdev->dev,
- HDMI_CODEC_DRV_NAME, 1,
- &codec_data,
- sizeof(codec_data));
- return PTR_ERR_OR_ZERO(dw->audio_pdev);
- }
- static int snd_dw_hdmi_remove(struct platform_device *pdev)
- {
- struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
- platform_device_unregister(dw->audio_pdev);
- return 0;
- }
- static struct platform_driver snd_dw_hdmi_driver = {
- .probe = snd_dw_hdmi_probe,
- .remove = snd_dw_hdmi_remove,
- .driver = {
- .name = DRIVER_NAME,
- },
- };
- module_platform_driver(snd_dw_hdmi_driver);
- MODULE_AUTHOR("Shengjiu Wang <[email protected]>");
- MODULE_DESCRIPTION("Synopsys Designware HDMI GPA ALSA interface");
- MODULE_LICENSE("GPL");
- MODULE_ALIAS("platform:" DRIVER_NAME);
|