Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- drivers/video/sh_mobile_hdmi.c | 153 ++++++++++++++++++++++++++++++++++++++++ include/video/sh_mobile_hdmi.h | 3 + 2 files changed, 156 insertions(+), 0 deletions(-)
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index d25e348..f31d570 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c @@ -22,6 +22,8 @@ #include <linux/slab.h> #include <linux/types.h> #include <linux/workqueue.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h>
#include <video/sh_mobile_hdmi.h> #include <video/sh_mobile_lcdc.h> @@ -202,6 +204,8 @@ enum hotplug_state { HDMI_HOTPLUG_EDID_DONE, };
+static struct snd_soc_codec *sh_hdmi_codec; + struct sh_hdmi { void __iomem *base; enum hotplug_state hp_state; @@ -210,6 +214,7 @@ struct sh_hdmi { struct fb_info *info; struct delayed_work edid_work; struct fb_var_screeninfo var; + struct snd_soc_codec codec; };
static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) @@ -222,6 +227,101 @@ static u8 hdmi_read(struct sh_hdmi *hdmi, u8 reg) return ioread8(hdmi->base + reg); }
+/************************************************************************ + + + HDMI sound + + +************************************************************************/ +static unsigned int sh_hdmi_snd_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + struct sh_hdmi *hdmi; + + codec = sh_hdmi_codec; + hdmi = codec->control_data; + + return hdmi_read(hdmi, reg); +} + +static int sh_hdmi_snd_write(struct snd_soc_codec *codec, + unsigned int reg, + unsigned int value) +{ + struct sh_hdmi *hdmi; + + codec = sh_hdmi_codec; + hdmi = codec->control_data; + + hdmi_write(hdmi, value, reg); + return 0; +} + +struct snd_soc_dai sh_hdmi_dai = { + .name = "SH MOBILE HDMI", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}; +EXPORT_SYMBOL_GPL(sh_hdmi_dai); + +/* + * initialise the driver + * register the mixer and dsp interfaces with the kernel + */ +static int sh_hdmi_snd_init(struct sh_hdmi *hdmi) +{ + struct snd_soc_codec *codec = &hdmi->codec; + int ret = 0; + + if (sh_hdmi_codec) { + dev_err(codec->dev, "Another hdmi is registered\n"); + return -EINVAL; + } + + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + snd_soc_codec_set_drvdata(codec, hdmi); + codec->name = "SH MOBILE HDMI"; + codec->owner = THIS_MODULE; + codec->read = sh_hdmi_snd_read; + codec->write = sh_hdmi_snd_write; + codec->dai = &sh_hdmi_dai; + codec->num_dai = 1; + + sh_hdmi_dai.dev = codec->dev; + sh_hdmi_codec = codec; + + ret = snd_soc_register_codec(codec); + if (ret) { + dev_err(codec->dev, "Failed to register codec: %d\n", ret); + return ret; + } + + ret = snd_soc_register_dai(&sh_hdmi_dai); + if (ret) { + dev_err(codec->dev, "Failed to register DAI: %d\n", ret); + snd_soc_unregister_codec(codec); + return ret; + } + + return ret; +} + +/************************************************************************ + + + HDMI video + + +************************************************************************/ /* External video parameter settings */ static void hdmi_external_video_param(struct sh_hdmi *hdmi) { @@ -792,6 +892,7 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); int irq = platform_get_irq(pdev, 0), ret; + struct snd_soc_codec *codec; struct sh_hdmi *hdmi; long rate;
@@ -806,6 +907,15 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)
hdmi->dev = &pdev->dev;
+ codec = &hdmi->codec; + codec->dev = &pdev->dev; + codec->control_data = hdmi; + ret = sh_hdmi_snd_init(hdmi); + if (ret < 0) { + dev_err(&pdev->dev, "failed to sound initialise\n"); + goto egetclk; + } + hdmi->hdmi_clk = clk_get(&pdev->dev, "ick"); if (IS_ERR(hdmi->hdmi_clk)) { ret = PTR_ERR(hdmi->hdmi_clk); @@ -901,6 +1011,10 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev) struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); int irq = platform_get_irq(pdev, 0);
+ snd_soc_unregister_dai(&sh_hdmi_dai); + snd_soc_unregister_codec(&hdmi->codec); + sh_hdmi_codec = NULL; + pdata->lcd_chan->board_cfg.display_on = NULL; pdata->lcd_chan->board_cfg.display_off = NULL; pdata->lcd_chan->board_cfg.board_data = NULL; @@ -917,6 +1031,45 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev) return 0; }
+static int sh_hdmi_snd_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + int ret; + + if (!sh_hdmi_codec) { + dev_err(&pdev->dev, "Codec device not registered\n"); + return -ENODEV; + } + + socdev->card->codec = sh_hdmi_codec; + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + dev_err(&pdev->dev, "failed to create pcms\n"); + return ret; + } + + dev_info(&pdev->dev, "sh_mobile_hdmi Audio Codec"); + return ret; +} + +static int sh_hdmi_snd_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_sh_hdmi = { + .probe = sh_hdmi_snd_probe, + .remove = sh_hdmi_snd_remove, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_sh_hdmi); + static struct platform_driver sh_hdmi_driver = { .remove = __exit_p(sh_hdmi_remove), .driver = { diff --git a/include/video/sh_mobile_hdmi.h b/include/video/sh_mobile_hdmi.h index 929c2d3..7b9fe18 100644 --- a/include/video/sh_mobile_hdmi.h +++ b/include/video/sh_mobile_hdmi.h @@ -35,4 +35,7 @@ struct sh_mobile_hdmi_info { unsigned int flags; };
+extern struct snd_soc_dai sh_hdmi_dai; +extern struct snd_soc_codec_device soc_codec_dev_sh_hdmi; + #endif