Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  1/*
  2 * dw-hdmi-i2s-audio.c
  3 *
  4 * Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
  5 *
  6 * This program is free software; you can redistribute it and/or modify
  7 * it under the terms of the GNU General Public License version 2 as
  8 * published by the Free Software Foundation.
  9 */
 10#include <drm/bridge/dw_hdmi.h>
 11
 12#include <sound/hdmi-codec.h>
 13
 14#include "dw-hdmi.h"
 15#include "dw-hdmi-audio.h"
 16
 17#define DRIVER_NAME "dw-hdmi-i2s-audio"
 18
 19static inline void hdmi_write(struct dw_hdmi_i2s_audio_data *audio,
 20			      u8 val, int offset)
 21{
 22	struct dw_hdmi *hdmi = audio->hdmi;
 23
 24	audio->write(hdmi, val, offset);
 25}
 26
 27static inline u8 hdmi_read(struct dw_hdmi_i2s_audio_data *audio, int offset)
 28{
 29	struct dw_hdmi *hdmi = audio->hdmi;
 30
 31	return audio->read(hdmi, offset);
 32}
 33
 34static int dw_hdmi_i2s_hw_params(struct device *dev, void *data,
 35				 struct hdmi_codec_daifmt *fmt,
 36				 struct hdmi_codec_params *hparms)
 37{
 38	struct dw_hdmi_i2s_audio_data *audio = data;
 39	struct dw_hdmi *hdmi = audio->hdmi;
 40	u8 conf0 = 0;
 41	u8 conf1 = 0;
 42	u8 inputclkfs = 0;
 43
 44	/* it cares I2S only */
 45	if ((fmt->fmt != HDMI_I2S) ||
 46	    (fmt->bit_clk_master | fmt->frame_clk_master)) {
 47		dev_err(dev, "unsupported format/settings\n");
 48		return -EINVAL;
 49	}
 50
 51	inputclkfs	= HDMI_AUD_INPUTCLKFS_64FS;
 52	conf0		= HDMI_AUD_CONF0_I2S_ALL_ENABLE;
 53
 54	switch (hparms->sample_width) {
 55	case 16:
 56		conf1 = HDMI_AUD_CONF1_WIDTH_16;
 57		break;
 58	case 24:
 59	case 32:
 60		conf1 = HDMI_AUD_CONF1_WIDTH_24;
 61		break;
 62	}
 63
 64	dw_hdmi_set_sample_rate(hdmi, hparms->sample_rate);
 65
 66	hdmi_write(audio, inputclkfs, HDMI_AUD_INPUTCLKFS);
 67	hdmi_write(audio, conf0, HDMI_AUD_CONF0);
 68	hdmi_write(audio, conf1, HDMI_AUD_CONF1);
 69
 70	dw_hdmi_audio_enable(hdmi);
 71
 72	return 0;
 73}
 74
 75static void dw_hdmi_i2s_audio_shutdown(struct device *dev, void *data)
 76{
 77	struct dw_hdmi_i2s_audio_data *audio = data;
 78	struct dw_hdmi *hdmi = audio->hdmi;
 79
 80	dw_hdmi_audio_disable(hdmi);
 81
 82	hdmi_write(audio, HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0);
 83}
 84
 85static struct hdmi_codec_ops dw_hdmi_i2s_ops = {
 86	.hw_params	= dw_hdmi_i2s_hw_params,
 87	.audio_shutdown	= dw_hdmi_i2s_audio_shutdown,
 88};
 89
 90static int snd_dw_hdmi_probe(struct platform_device *pdev)
 91{
 92	struct dw_hdmi_i2s_audio_data *audio = pdev->dev.platform_data;
 93	struct platform_device_info pdevinfo;
 94	struct hdmi_codec_pdata pdata;
 95	struct platform_device *platform;
 96
 97	pdata.ops		= &dw_hdmi_i2s_ops;
 98	pdata.i2s		= 1;
 99	pdata.max_i2s_channels	= 6;
100	pdata.data		= audio;
101
102	memset(&pdevinfo, 0, sizeof(pdevinfo));
103	pdevinfo.parent		= pdev->dev.parent;
104	pdevinfo.id		= PLATFORM_DEVID_AUTO;
105	pdevinfo.name		= HDMI_CODEC_DRV_NAME;
106	pdevinfo.data		= &pdata;
107	pdevinfo.size_data	= sizeof(pdata);
108	pdevinfo.dma_mask	= DMA_BIT_MASK(32);
109
110	platform = platform_device_register_full(&pdevinfo);
111	if (IS_ERR(platform))
112		return PTR_ERR(platform);
113
114	dev_set_drvdata(&pdev->dev, platform);
115
116	return 0;
117}
118
119static int snd_dw_hdmi_remove(struct platform_device *pdev)
120{
121	struct platform_device *platform = dev_get_drvdata(&pdev->dev);
122
123	platform_device_unregister(platform);
124
125	return 0;
126}
127
128static struct platform_driver snd_dw_hdmi_driver = {
129	.probe	= snd_dw_hdmi_probe,
130	.remove	= snd_dw_hdmi_remove,
131	.driver	= {
132		.name = DRIVER_NAME,
133		.owner = THIS_MODULE,
134	},
135};
136module_platform_driver(snd_dw_hdmi_driver);
137
138MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
139MODULE_DESCRIPTION("Synopsis Designware HDMI I2S ALSA SoC interface");
140MODULE_LICENSE("GPL v2");
141MODULE_ALIAS("platform:" DRIVER_NAME);