Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
0002 /*
0003  * dw-hdmi-gp-audio.c
0004  *
0005  * Copyright 2020-2022 NXP
0006  */
0007 #include <linux/io.h>
0008 #include <linux/interrupt.h>
0009 #include <linux/module.h>
0010 #include <linux/platform_device.h>
0011 #include <linux/dmaengine.h>
0012 #include <linux/dma-mapping.h>
0013 #include <drm/bridge/dw_hdmi.h>
0014 #include <drm/drm_edid.h>
0015 #include <drm/drm_connector.h>
0016 
0017 #include <sound/hdmi-codec.h>
0018 #include <sound/asoundef.h>
0019 #include <sound/core.h>
0020 #include <sound/initval.h>
0021 #include <sound/pcm.h>
0022 #include <sound/pcm_drm_eld.h>
0023 #include <sound/pcm_iec958.h>
0024 #include <sound/dmaengine_pcm.h>
0025 
0026 #include "dw-hdmi-audio.h"
0027 
0028 #define DRIVER_NAME "dw-hdmi-gp-audio"
0029 #define DRV_NAME    "hdmi-gp-audio"
0030 
0031 struct snd_dw_hdmi {
0032     struct dw_hdmi_audio_data data;
0033     struct platform_device  *audio_pdev;
0034     unsigned int pos;
0035 };
0036 
0037 struct dw_hdmi_channel_conf {
0038     u8 conf1;
0039     u8 ca;
0040 };
0041 
0042 /*
0043  * The default mapping of ALSA channels to HDMI channels and speaker
0044  * allocation bits.  Note that we can't do channel remapping here -
0045  * channels must be in the same order.
0046  *
0047  * Mappings for alsa-lib pcm/surround*.conf files:
0048  *
0049  *      Front   Sur4.0  Sur4.1  Sur5.0  Sur5.1  Sur7.1
0050  * Channels 2   4   6   6   6   8
0051  *
0052  * Our mapping from ALSA channel to CEA686D speaker name and HDMI channel:
0053  *
0054  *              Number of ALSA channels
0055  * ALSA Channel 2   3   4   5   6   7   8
0056  * 0        FL:0    =   =   =   =   =   =
0057  * 1        FR:1    =   =   =   =   =   =
0058  * 2            FC:3    RL:4    LFE:2   =   =   =
0059  * 3                RR:5    RL:4    FC:3    =   =
0060  * 4                    RR:5    RL:4    =   =
0061  * 5                        RR:5    =   =
0062  * 6                            RC:6    =
0063  * 7                            RLC/FRC RLC/FRC
0064  */
0065 static struct dw_hdmi_channel_conf default_hdmi_channel_config[7] = {
0066     { 0x03, 0x00 }, /* FL,FR */
0067     { 0x0b, 0x02 }, /* FL,FR,FC */
0068     { 0x33, 0x08 }, /* FL,FR,RL,RR */
0069     { 0x37, 0x09 }, /* FL,FR,LFE,RL,RR */
0070     { 0x3f, 0x0b }, /* FL,FR,LFE,FC,RL,RR */
0071     { 0x7f, 0x0f }, /* FL,FR,LFE,FC,RL,RR,RC */
0072     { 0xff, 0x13 }, /* FL,FR,LFE,FC,RL,RR,[FR]RC,[FR]LC */
0073 };
0074 
0075 static int audio_hw_params(struct device *dev,  void *data,
0076                struct hdmi_codec_daifmt *daifmt,
0077                struct hdmi_codec_params *params)
0078 {
0079     struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
0080     u8 ca;
0081 
0082     dw_hdmi_set_sample_rate(dw->data.hdmi, params->sample_rate);
0083 
0084     ca = default_hdmi_channel_config[params->channels - 2].ca;
0085 
0086     dw_hdmi_set_channel_count(dw->data.hdmi, params->channels);
0087     dw_hdmi_set_channel_allocation(dw->data.hdmi, ca);
0088 
0089     dw_hdmi_set_sample_non_pcm(dw->data.hdmi,
0090                    params->iec.status[0] & IEC958_AES0_NONAUDIO);
0091     dw_hdmi_set_sample_width(dw->data.hdmi, params->sample_width);
0092 
0093     return 0;
0094 }
0095 
0096 static void audio_shutdown(struct device *dev, void *data)
0097 {
0098 }
0099 
0100 static int audio_mute_stream(struct device *dev, void *data,
0101                  bool enable, int direction)
0102 {
0103     struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
0104 
0105     if (!enable)
0106         dw_hdmi_audio_enable(dw->data.hdmi);
0107     else
0108         dw_hdmi_audio_disable(dw->data.hdmi);
0109 
0110     return 0;
0111 }
0112 
0113 static int audio_get_eld(struct device *dev, void *data,
0114              u8 *buf, size_t len)
0115 {
0116     struct dw_hdmi_audio_data *audio = data;
0117     u8 *eld;
0118 
0119     eld = audio->get_eld(audio->hdmi);
0120     if (eld)
0121         memcpy(buf, eld, min_t(size_t, MAX_ELD_BYTES, len));
0122     else
0123         /* Pass en empty ELD if connector not available */
0124         memset(buf, 0, len);
0125 
0126     return 0;
0127 }
0128 
0129 static int audio_hook_plugged_cb(struct device *dev, void *data,
0130                  hdmi_codec_plugged_cb fn,
0131                  struct device *codec_dev)
0132 {
0133     struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
0134 
0135     return dw_hdmi_set_plugged_cb(dw->data.hdmi, fn, codec_dev);
0136 }
0137 
0138 static const struct hdmi_codec_ops audio_codec_ops = {
0139     .hw_params = audio_hw_params,
0140     .audio_shutdown = audio_shutdown,
0141     .mute_stream = audio_mute_stream,
0142     .get_eld = audio_get_eld,
0143     .hook_plugged_cb = audio_hook_plugged_cb,
0144 };
0145 
0146 static int snd_dw_hdmi_probe(struct platform_device *pdev)
0147 {
0148     struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
0149     struct snd_dw_hdmi *dw;
0150 
0151     const struct hdmi_codec_pdata codec_data = {
0152         .i2s = 1,
0153         .spdif = 0,
0154         .ops = &audio_codec_ops,
0155         .max_i2s_channels = 8,
0156         .data = data,
0157     };
0158 
0159     dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);
0160     if (!dw)
0161         return -ENOMEM;
0162 
0163     dw->data = *data;
0164 
0165     platform_set_drvdata(pdev, dw);
0166 
0167     dw->audio_pdev = platform_device_register_data(&pdev->dev,
0168                                HDMI_CODEC_DRV_NAME, 1,
0169                                &codec_data,
0170                                sizeof(codec_data));
0171 
0172     return PTR_ERR_OR_ZERO(dw->audio_pdev);
0173 }
0174 
0175 static int snd_dw_hdmi_remove(struct platform_device *pdev)
0176 {
0177     struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
0178 
0179     platform_device_unregister(dw->audio_pdev);
0180 
0181     return 0;
0182 }
0183 
0184 static struct platform_driver snd_dw_hdmi_driver = {
0185     .probe  = snd_dw_hdmi_probe,
0186     .remove = snd_dw_hdmi_remove,
0187     .driver = {
0188         .name = DRIVER_NAME,
0189     },
0190 };
0191 
0192 module_platform_driver(snd_dw_hdmi_driver);
0193 
0194 MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
0195 MODULE_DESCRIPTION("Synopsys Designware HDMI GPA ALSA interface");
0196 MODULE_LICENSE("GPL");
0197 MODULE_ALIAS("platform:" DRIVER_NAME);