Path: blob/master/drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c
51706 views
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)1/*2* dw-hdmi-gp-audio.c3*4* Copyright 2020-2022 NXP5*/6#include <linux/io.h>7#include <linux/interrupt.h>8#include <linux/module.h>9#include <linux/platform_device.h>10#include <linux/dmaengine.h>11#include <linux/dma-mapping.h>12#include <drm/bridge/dw_hdmi.h>13#include <drm/drm_edid.h>14#include <drm/drm_connector.h>1516#include <sound/hdmi-codec.h>17#include <sound/asoundef.h>18#include <sound/core.h>19#include <sound/initval.h>20#include <sound/pcm.h>21#include <sound/pcm_drm_eld.h>22#include <sound/pcm_iec958.h>23#include <sound/dmaengine_pcm.h>2425#include "dw-hdmi-audio.h"2627#define DRIVER_NAME "dw-hdmi-gp-audio"28#define DRV_NAME "hdmi-gp-audio"2930struct snd_dw_hdmi {31struct dw_hdmi_audio_data data;32struct platform_device *audio_pdev;33unsigned int pos;34};3536struct dw_hdmi_channel_conf {37u8 conf1;38u8 ca;39};4041/*42* The default mapping of ALSA channels to HDMI channels and speaker43* allocation bits. Note that we can't do channel remapping here -44* channels must be in the same order.45*46* Mappings for alsa-lib pcm/surround*.conf files:47*48* Front Sur4.0 Sur4.1 Sur5.0 Sur5.1 Sur7.149* Channels 2 4 6 6 6 850*51* Our mapping from ALSA channel to CEA686D speaker name and HDMI channel:52*53* Number of ALSA channels54* ALSA Channel 2 3 4 5 6 7 855* 0 FL:0 = = = = = =56* 1 FR:1 = = = = = =57* 2 FC:3 RL:4 LFE:2 = = =58* 3 RR:5 RL:4 FC:3 = =59* 4 RR:5 RL:4 = =60* 5 RR:5 = =61* 6 RC:6 =62* 7 RLC/FRC RLC/FRC63*/64static struct dw_hdmi_channel_conf default_hdmi_channel_config[7] = {65{ 0x03, 0x00 }, /* FL,FR */66{ 0x0b, 0x02 }, /* FL,FR,FC */67{ 0x33, 0x08 }, /* FL,FR,RL,RR */68{ 0x37, 0x09 }, /* FL,FR,LFE,RL,RR */69{ 0x3f, 0x0b }, /* FL,FR,LFE,FC,RL,RR */70{ 0x7f, 0x0f }, /* FL,FR,LFE,FC,RL,RR,RC */71{ 0xff, 0x13 }, /* FL,FR,LFE,FC,RL,RR,[FR]RC,[FR]LC */72};7374static int audio_hw_params(struct device *dev, void *data,75struct hdmi_codec_daifmt *daifmt,76struct hdmi_codec_params *params)77{78struct snd_dw_hdmi *dw = dev_get_drvdata(dev);79u8 ca;8081dw_hdmi_set_sample_rate(dw->data.hdmi, params->sample_rate);8283ca = default_hdmi_channel_config[params->channels - 2].ca;8485dw_hdmi_set_channel_count(dw->data.hdmi, params->channels);86dw_hdmi_set_channel_allocation(dw->data.hdmi, ca);8788dw_hdmi_set_sample_non_pcm(dw->data.hdmi,89params->iec.status[0] & IEC958_AES0_NONAUDIO);90dw_hdmi_set_sample_width(dw->data.hdmi, params->sample_width);9192if (daifmt->bit_fmt == SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE)93dw_hdmi_set_sample_iec958(dw->data.hdmi, 1);94else95dw_hdmi_set_sample_iec958(dw->data.hdmi, 0);9697return 0;98}99100static void audio_shutdown(struct device *dev, void *data)101{102}103104static int audio_mute_stream(struct device *dev, void *data,105bool enable, int direction)106{107struct snd_dw_hdmi *dw = dev_get_drvdata(dev);108109if (!enable)110dw_hdmi_audio_enable(dw->data.hdmi);111else112dw_hdmi_audio_disable(dw->data.hdmi);113114return 0;115}116117static int audio_get_eld(struct device *dev, void *data,118u8 *buf, size_t len)119{120struct dw_hdmi_audio_data *audio = data;121u8 *eld;122123eld = audio->get_eld(audio->hdmi);124if (eld)125memcpy(buf, eld, min_t(size_t, MAX_ELD_BYTES, len));126else127/* Pass en empty ELD if connector not available */128memset(buf, 0, len);129130return 0;131}132133static int audio_hook_plugged_cb(struct device *dev, void *data,134hdmi_codec_plugged_cb fn,135struct device *codec_dev)136{137struct snd_dw_hdmi *dw = dev_get_drvdata(dev);138139return dw_hdmi_set_plugged_cb(dw->data.hdmi, fn, codec_dev);140}141142static const struct hdmi_codec_ops audio_codec_ops = {143.hw_params = audio_hw_params,144.audio_shutdown = audio_shutdown,145.mute_stream = audio_mute_stream,146.get_eld = audio_get_eld,147.hook_plugged_cb = audio_hook_plugged_cb,148};149150static int snd_dw_hdmi_probe(struct platform_device *pdev)151{152struct dw_hdmi_audio_data *data = pdev->dev.platform_data;153struct snd_dw_hdmi *dw;154155const struct hdmi_codec_pdata codec_data = {156.i2s = 1,157.spdif = 0,158.ops = &audio_codec_ops,159.max_i2s_channels = 8,160.data = data,161};162163dw = devm_kzalloc(&pdev->dev, sizeof(*dw), GFP_KERNEL);164if (!dw)165return -ENOMEM;166167dw->data = *data;168169platform_set_drvdata(pdev, dw);170171dw->audio_pdev = platform_device_register_data(&pdev->dev,172HDMI_CODEC_DRV_NAME, 1,173&codec_data,174sizeof(codec_data));175176return PTR_ERR_OR_ZERO(dw->audio_pdev);177}178179static void snd_dw_hdmi_remove(struct platform_device *pdev)180{181struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);182183platform_device_unregister(dw->audio_pdev);184}185186static struct platform_driver snd_dw_hdmi_driver = {187.probe = snd_dw_hdmi_probe,188.remove = snd_dw_hdmi_remove,189.driver = {190.name = DRIVER_NAME,191},192};193194module_platform_driver(snd_dw_hdmi_driver);195196MODULE_AUTHOR("Shengjiu Wang <[email protected]>");197MODULE_DESCRIPTION("Synopsys Designware HDMI GPA ALSA interface");198MODULE_LICENSE("GPL");199MODULE_ALIAS("platform:" DRIVER_NAME);200201202