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