Merge branch 'linux-linaro-lsk-v4.4-android' of git://git.linaro.org/kernel/linux...
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / bridge / dw-hdmi-i2s-audio.c
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
19 static inline void hdmi_write(struct dw_hdmi_i2s_audio_data *audio, u8 val, int offset)
20 {
21         struct dw_hdmi *hdmi = audio->hdmi;
22
23         audio->write(hdmi, val, offset);
24 }
25
26 static inline u8 hdmi_read(struct dw_hdmi_i2s_audio_data *audio, int offset)
27 {
28         struct dw_hdmi *hdmi = audio->hdmi;
29
30         return audio->read(hdmi, offset);
31 }
32
33 static int dw_hdmi_i2s_hw_params(struct device *dev, void *data,
34                                  struct hdmi_codec_daifmt *fmt,
35                                  struct hdmi_codec_params *hparms)
36 {
37         struct dw_hdmi_i2s_audio_data *audio = data;
38         struct dw_hdmi *hdmi = audio->hdmi;
39         u8 conf0 = 0;
40         u8 conf1 = 0;
41         u8 inputclkfs = 0;
42
43         /* it cares I2S only */
44         if ((fmt->fmt != HDMI_I2S) ||
45             (fmt->bit_clk_master | fmt->frame_clk_master)) {
46                 dev_err(dev, "unsupported format/settings\n");
47                 return -EINVAL;
48         }
49
50         inputclkfs      = HDMI_AUD_INPUTCLKFS_64FS;
51
52         switch (hparms->sample_width) {
53         case 16:
54                 conf1 = HDMI_AUD_CONF1_WIDTH_16;
55                 break;
56         case 24:
57         case 32:
58                 conf1 = HDMI_AUD_CONF1_WIDTH_24;
59                 break;
60         default:
61                 dev_err(dev, "unsupported sample width [%d]\n", hparms->sample_width);
62                 return -EINVAL;
63         }
64
65         switch (hparms->channels) {
66         case 2:
67                 conf0 = HDMI_AUD_CONF0_I2S_2CHANNEL_ENABLE;
68                 break;
69         case 4:
70                 conf0 = HDMI_AUD_CONF0_I2S_4CHANNEL_ENABLE;
71                 break;
72         case 6:
73                 conf0 = HDMI_AUD_CONF0_I2S_6CHANNEL_ENABLE;
74                 break;
75         case 8:
76                 conf0 = HDMI_AUD_CONF0_I2S_8CHANNEL_ENABLE;
77                 break;
78         default:
79                 dev_err(dev, "unsupported channels [%d]\n", hparms->channels);
80                 return -EINVAL;
81         }
82
83         dw_hdmi_set_sample_rate(hdmi, hparms->sample_rate);
84
85         hdmi_write(audio, inputclkfs, HDMI_AUD_INPUTCLKFS);
86         hdmi_write(audio, conf0, HDMI_AUD_CONF0);
87         hdmi_write(audio, conf1, HDMI_AUD_CONF1);
88
89         dw_hdmi_audio_enable(hdmi);
90
91         return 0;
92 }
93
94 static void dw_hdmi_i2s_audio_shutdown(struct device *dev, void *data)
95 {
96         struct dw_hdmi_i2s_audio_data *audio = data;
97         struct dw_hdmi *hdmi = audio->hdmi;
98
99         dw_hdmi_audio_disable(hdmi);
100
101         hdmi_write(audio, HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0);
102 }
103
104 static struct hdmi_codec_ops dw_hdmi_i2s_ops = {
105         .hw_params      = dw_hdmi_i2s_hw_params,
106         .audio_shutdown = dw_hdmi_i2s_audio_shutdown,
107 };
108
109 static int snd_dw_hdmi_probe(struct platform_device *pdev)
110 {
111         struct dw_hdmi_i2s_audio_data *audio = pdev->dev.platform_data;
112         struct platform_device_info pdevinfo;
113         struct hdmi_codec_pdata pdata;
114
115         pdata.ops               = &dw_hdmi_i2s_ops;
116         pdata.i2s               = 1;
117         pdata.max_i2s_channels  = 8;
118         pdata.data              = audio;
119
120         memset(&pdevinfo, 0, sizeof(pdevinfo));
121         pdevinfo.parent         = pdev->dev.parent;
122         pdevinfo.id             = PLATFORM_DEVID_AUTO;
123         pdevinfo.name           = HDMI_CODEC_DRV_NAME;
124         pdevinfo.data           = &pdata;
125         pdevinfo.size_data      = sizeof(pdata);
126         pdevinfo.dma_mask       = DMA_BIT_MASK(32);
127
128         return IS_ERR_OR_NULL(platform_device_register_full(&pdevinfo));
129 }
130
131 static struct platform_driver snd_dw_hdmi_driver = {
132         .probe  = snd_dw_hdmi_probe,
133         .driver = {
134                 .name = DRIVER_NAME,
135                 .owner = THIS_MODULE,
136         },
137 };
138 module_platform_driver(snd_dw_hdmi_driver);
139
140 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
141 MODULE_DESCRIPTION("Synopsis Designware HDMI I2S ALSA SoC interface");
142 MODULE_LICENSE("GPL v2");
143 MODULE_ALIAS("platform:" DRIVER_NAME);