blob: 8a0744b58a329aff24a620aca07feb4f00b1a32f [file] [log] [blame]
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +05301/*
Vaibhav Agarwal098dfaf2016-07-22 09:41:30 +05302 * APBridge ALSA SoC dummy codec driver
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +05303 * Copyright 2016 Google Inc.
4 * Copyright 2016 Linaro Ltd.
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +05305 *
6 * Released under the GPLv2 only.
7 */
Vaibhav Agarwal2a70e492016-01-13 14:07:50 -07008#include <linux/kernel.h>
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +05309#include <linux/module.h>
Vaibhav Agarwalf2bf63a2016-04-22 11:13:19 -070010#include <linux/pm_runtime.h>
Vaibhav Agarwal2a70e492016-01-13 14:07:50 -070011#include <sound/soc.h>
12#include <sound/pcm_params.h>
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +053013#include <uapi/linux/input.h>
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +053014
Vaibhav Agarwal78853422016-01-13 14:07:49 -070015#include "audio_codec.h"
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -070016#include "audio_apbridgea.h"
Vaibhav Agarwal17247da2016-01-13 14:07:53 -070017#include "audio_manager.h"
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +053018
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +053019static struct gbaudio_codec_info *gbcodec;
Vaibhav Agarwal2a70e492016-01-13 14:07:50 -070020
Viresh Kumar36460e82016-04-21 08:11:57 +053021static struct gbaudio_data_connection *
Vaibhav Agarwal60e73272016-08-04 15:14:35 +053022find_data(struct gbaudio_module_info *module, int id)
Vaibhav Agarwal796fad42016-01-28 21:15:39 +053023{
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +053024 struct gbaudio_data_connection *data;
Vaibhav Agarwal796fad42016-01-28 21:15:39 +053025
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +053026 list_for_each_entry(data, &module->data_list, list) {
Vaibhav Agarwal60e73272016-08-04 15:14:35 +053027 if (id == data->id)
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +053028 return data;
Vaibhav Agarwal796fad42016-01-28 21:15:39 +053029 }
30 return NULL;
31}
32
Vaibhav Agarwal19866602016-08-04 15:14:38 +053033static struct gbaudio_stream_params *
34find_dai_stream_params(struct gbaudio_codec_info *codec, int id, int stream)
35{
36 struct gbaudio_codec_dai *dai;
37
38 list_for_each_entry(dai, &codec->dai_list, list) {
39 if (dai->id == id)
40 return &dai->params[stream];
41 }
42 return NULL;
43}
44
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +053045static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec,
Vaibhav Agarwal60e73272016-08-04 15:14:35 +053046 struct gbaudio_module_info *module, int id)
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +053047{
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +053048 int module_state, ret = 0;
Chaehyun Lim79cb2b262016-09-20 09:47:30 +090049 u16 data_cport, i2s_port, cportid;
Chaehyun Lim31959392016-09-20 09:47:29 +090050 u8 sig_bits, channels;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +053051 uint32_t format, rate;
52 struct gbaudio_data_connection *data;
Vaibhav Agarwal19866602016-08-04 15:14:38 +053053 struct gbaudio_stream_params *params;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +053054
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +053055 /* find the dai */
Vaibhav Agarwal60e73272016-08-04 15:14:35 +053056 data = find_data(module, id);
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +053057 if (!data) {
Vaibhav Agarwal60e73272016-08-04 15:14:35 +053058 dev_err(module->dev, "%d:DATA connection missing\n", id);
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +053059 return -ENODEV;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +053060 }
Vaibhav Agarwal591c4522016-08-04 15:14:39 +053061 module_state = data->state[SNDRV_PCM_STREAM_PLAYBACK];
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +053062
Vaibhav Agarwal19866602016-08-04 15:14:38 +053063 params = find_dai_stream_params(codec, id, SNDRV_PCM_STREAM_PLAYBACK);
64 if (!params) {
65 dev_err(codec->dev, "Failed to fetch dai_stream pointer\n");
66 return -EINVAL;
67 }
68
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +053069 /* register cport */
70 if (module_state < GBAUDIO_CODEC_STARTUP) {
71 i2s_port = 0; /* fixed for now */
72 cportid = data->connection->hd_cport_id;
73 ret = gb_audio_apbridgea_register_cport(data->connection,
74 i2s_port, cportid,
75 AUDIO_APBRIDGEA_DIRECTION_TX);
76 if (ret) {
77 dev_err_ratelimited(module->dev,
78 "reg_cport failed:%d\n", ret);
79 return ret;
80 }
Vaibhav Agarwal591c4522016-08-04 15:14:39 +053081 data->state[SNDRV_PCM_STREAM_PLAYBACK] =
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +053082 GBAUDIO_CODEC_STARTUP;
Vaibhav Agarwal19866602016-08-04 15:14:38 +053083 dev_dbg(module->dev, "Dynamic Register %d DAI\n", cportid);
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +053084 }
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +053085
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +053086 /* hw_params */
87 if (module_state < GBAUDIO_CODEC_HWPARAMS) {
Vaibhav Agarwal19866602016-08-04 15:14:38 +053088 format = params->format;
89 channels = params->channels;
90 rate = params->rate;
91 sig_bits = params->sig_bits;
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +053092 data_cport = data->connection->intf_cport_id;
93 ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport,
94 format, rate, channels, sig_bits);
95 if (ret) {
96 dev_err_ratelimited(module->dev, "set_pcm failed:%d\n",
97 ret);
98 return ret;
99 }
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530100 data->state[SNDRV_PCM_STREAM_PLAYBACK] =
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530101 GBAUDIO_CODEC_HWPARAMS;
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530102 dev_dbg(module->dev, "Dynamic hw_params %d DAI\n", data_cport);
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530103 }
104
105 /* prepare */
106 if (module_state < GBAUDIO_CODEC_PREPARE) {
107 data_cport = data->connection->intf_cport_id;
108 ret = gb_audio_gb_set_tx_data_size(module->mgmt_connection,
109 data_cport, 192);
110 if (ret) {
111 dev_err_ratelimited(module->dev,
112 "set_tx_data_size failed:%d\n",
113 ret);
114 return ret;
115 }
116 ret = gb_audio_gb_activate_tx(module->mgmt_connection,
117 data_cport);
118 if (ret) {
119 dev_err_ratelimited(module->dev,
120 "activate_tx failed:%d\n", ret);
121 return ret;
122 }
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530123 data->state[SNDRV_PCM_STREAM_PLAYBACK] =
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530124 GBAUDIO_CODEC_PREPARE;
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530125 dev_dbg(module->dev, "Dynamic prepare %d DAI\n", data_cport);
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530126 }
127
128 return 0;
129}
130
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530131static int gbaudio_module_disable_tx(struct gbaudio_module_info *module, int id)
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530132{
133 int ret;
Chaehyun Lim79cb2b262016-09-20 09:47:30 +0900134 u16 data_cport, cportid, i2s_port;
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530135 int module_state;
136 struct gbaudio_data_connection *data;
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530137
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530138 /* find the dai */
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530139 data = find_data(module, id);
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530140 if (!data) {
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530141 dev_err(module->dev, "%d:DATA connection missing\n", id);
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530142 return -ENODEV;
143 }
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530144 module_state = data->state[SNDRV_PCM_STREAM_PLAYBACK];
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530145
146 if (module_state > GBAUDIO_CODEC_HWPARAMS) {
147 data_cport = data->connection->intf_cport_id;
148 ret = gb_audio_gb_deactivate_tx(module->mgmt_connection,
149 data_cport);
150 if (ret) {
151 dev_err_ratelimited(module->dev,
152 "deactivate_tx failed:%d\n", ret);
153 return ret;
154 }
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530155 dev_dbg(module->dev, "Dynamic deactivate %d DAI\n", data_cport);
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530156 data->state[SNDRV_PCM_STREAM_PLAYBACK] =
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530157 GBAUDIO_CODEC_HWPARAMS;
158 }
159
160 if (module_state > GBAUDIO_CODEC_SHUTDOWN) {
161 i2s_port = 0; /* fixed for now */
162 cportid = data->connection->hd_cport_id;
163 ret = gb_audio_apbridgea_unregister_cport(data->connection,
164 i2s_port, cportid,
165 AUDIO_APBRIDGEA_DIRECTION_TX);
166 if (ret) {
167 dev_err_ratelimited(module->dev,
168 "unregister_cport failed:%d\n",
169 ret);
170 return ret;
171 }
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530172 dev_dbg(module->dev, "Dynamic Unregister %d DAI\n", cportid);
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530173 data->state[SNDRV_PCM_STREAM_PLAYBACK] =
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530174 GBAUDIO_CODEC_SHUTDOWN;
175 }
176
177 return 0;
178}
179
180static int gbaudio_module_enable_rx(struct gbaudio_codec_info *codec,
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530181 struct gbaudio_module_info *module, int id)
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530182{
183 int module_state, ret = 0;
Chaehyun Lim79cb2b262016-09-20 09:47:30 +0900184 u16 data_cport, i2s_port, cportid;
Chaehyun Lim31959392016-09-20 09:47:29 +0900185 u8 sig_bits, channels;
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530186 uint32_t format, rate;
187 struct gbaudio_data_connection *data;
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530188 struct gbaudio_stream_params *params;
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530189
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530190 /* find the dai */
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530191 data = find_data(module, id);
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530192 if (!data) {
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530193 dev_err(module->dev, "%d:DATA connection missing\n", id);
Vaibhav Agarwal90579d42016-08-04 15:14:28 +0530194 return -ENODEV;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530195 }
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530196 module_state = data->state[SNDRV_PCM_STREAM_CAPTURE];
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530197
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530198 params = find_dai_stream_params(codec, id, SNDRV_PCM_STREAM_CAPTURE);
199 if (!params) {
200 dev_err(codec->dev, "Failed to fetch dai_stream pointer\n");
201 return -EINVAL;
202 }
203
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530204 /* register cport */
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530205 if (module_state < GBAUDIO_CODEC_STARTUP) {
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530206 i2s_port = 0; /* fixed for now */
207 cportid = data->connection->hd_cport_id;
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530208 ret = gb_audio_apbridgea_register_cport(data->connection,
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530209 i2s_port, cportid,
Mark Greerf0ec8cd2016-08-20 16:25:06 -0700210 AUDIO_APBRIDGEA_DIRECTION_RX);
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530211 if (ret) {
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530212 dev_err_ratelimited(module->dev,
213 "reg_cport failed:%d\n", ret);
214 return ret;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530215 }
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530216 data->state[SNDRV_PCM_STREAM_CAPTURE] =
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530217 GBAUDIO_CODEC_STARTUP;
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530218 dev_dbg(module->dev, "Dynamic Register %d DAI\n", cportid);
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530219 }
220
221 /* hw_params */
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530222 if (module_state < GBAUDIO_CODEC_HWPARAMS) {
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530223 format = params->format;
224 channels = params->channels;
225 rate = params->rate;
226 sig_bits = params->sig_bits;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530227 data_cport = data->connection->intf_cport_id;
228 ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport,
229 format, rate, channels, sig_bits);
230 if (ret) {
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530231 dev_err_ratelimited(module->dev, "set_pcm failed:%d\n",
232 ret);
233 return ret;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530234 }
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530235 data->state[SNDRV_PCM_STREAM_CAPTURE] =
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530236 GBAUDIO_CODEC_HWPARAMS;
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530237 dev_dbg(module->dev, "Dynamic hw_params %d DAI\n", data_cport);
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530238 }
239
240 /* prepare */
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530241 if (module_state < GBAUDIO_CODEC_PREPARE) {
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530242 data_cport = data->connection->intf_cport_id;
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530243 ret = gb_audio_gb_set_rx_data_size(module->mgmt_connection,
244 data_cport, 192);
245 if (ret) {
246 dev_err_ratelimited(module->dev,
247 "set_rx_data_size failed:%d\n",
248 ret);
249 return ret;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530250 }
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530251 ret = gb_audio_gb_activate_rx(module->mgmt_connection,
252 data_cport);
253 if (ret) {
254 dev_err_ratelimited(module->dev,
255 "activate_rx failed:%d\n", ret);
256 return ret;
257 }
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530258 data->state[SNDRV_PCM_STREAM_CAPTURE] =
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530259 GBAUDIO_CODEC_PREPARE;
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530260 dev_dbg(module->dev, "Dynamic prepare %d DAI\n", data_cport);
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530261 }
262
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530263 return 0;
264}
265
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530266static int gbaudio_module_disable_rx(struct gbaudio_module_info *module, int id)
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530267{
268 int ret;
Chaehyun Lim79cb2b262016-09-20 09:47:30 +0900269 u16 data_cport, cportid, i2s_port;
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530270 int module_state;
271 struct gbaudio_data_connection *data;
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530272
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530273 /* find the dai */
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530274 data = find_data(module, id);
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530275 if (!data) {
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530276 dev_err(module->dev, "%d:DATA connection missing\n", id);
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530277 return -ENODEV;
278 }
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530279 module_state = data->state[SNDRV_PCM_STREAM_CAPTURE];
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530280
281 if (module_state > GBAUDIO_CODEC_HWPARAMS) {
282 data_cport = data->connection->intf_cport_id;
283 ret = gb_audio_gb_deactivate_rx(module->mgmt_connection,
284 data_cport);
285 if (ret) {
286 dev_err_ratelimited(module->dev,
287 "deactivate_rx failed:%d\n", ret);
288 return ret;
289 }
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530290 dev_dbg(module->dev, "Dynamic deactivate %d DAI\n", data_cport);
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530291 data->state[SNDRV_PCM_STREAM_CAPTURE] =
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530292 GBAUDIO_CODEC_HWPARAMS;
293 }
294
295 if (module_state > GBAUDIO_CODEC_SHUTDOWN) {
296 i2s_port = 0; /* fixed for now */
297 cportid = data->connection->hd_cport_id;
298 ret = gb_audio_apbridgea_unregister_cport(data->connection,
299 i2s_port, cportid,
300 AUDIO_APBRIDGEA_DIRECTION_RX);
301 if (ret) {
302 dev_err_ratelimited(module->dev,
303 "unregister_cport failed:%d\n",
304 ret);
305 return ret;
306 }
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530307 dev_dbg(module->dev, "Dynamic Unregister %d DAI\n", cportid);
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530308 data->state[SNDRV_PCM_STREAM_CAPTURE] =
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530309 GBAUDIO_CODEC_SHUTDOWN;
310 }
311
312 return 0;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530313}
314
315int gbaudio_module_update(struct gbaudio_codec_info *codec,
Vaibhav Agarwal4ffca622016-08-04 15:14:32 +0530316 struct snd_soc_dapm_widget *w,
317 struct gbaudio_module_info *module, int enable)
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530318{
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530319 int dai_id, ret;
320 char intf_name[NAME_SIZE], dir[NAME_SIZE];
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530321
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530322 dev_dbg(module->dev, "%s:Module update %s sequence\n", w->name,
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530323 enable ? "Enable":"Disable");
324
Richard Groux0c8d9c72016-09-21 19:05:29 +0200325 if ((w->id != snd_soc_dapm_aif_in) && (w->id != snd_soc_dapm_aif_out)) {
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530326 dev_dbg(codec->dev, "No action required for %s\n", w->name);
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530327 return 0;
328 }
329
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530330 /* parse dai_id from AIF widget's stream_name */
331 ret = sscanf(w->sname, "%s %d %s", intf_name, &dai_id, dir);
332 if (ret < 3) {
333 dev_err(codec->dev, "Error while parsing dai_id for %s\n",
334 w->name);
335 return -EINVAL;
336 }
337
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530338 mutex_lock(&codec->lock);
Vaibhav Agarwal487dcbd2016-08-04 15:14:33 +0530339 if (w->id == snd_soc_dapm_aif_in) {
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530340 if (enable)
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530341 ret = gbaudio_module_enable_tx(codec, module, dai_id);
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530342 else
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530343 ret = gbaudio_module_disable_tx(module, dai_id);
Vaibhav Agarwal487dcbd2016-08-04 15:14:33 +0530344 } else if (w->id == snd_soc_dapm_aif_out) {
Vaibhav Agarwal094c4302016-03-29 16:32:37 +0530345 if (enable)
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530346 ret = gbaudio_module_enable_rx(codec, module, dai_id);
Vaibhav Agarwal094c4302016-03-29 16:32:37 +0530347 else
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530348 ret = gbaudio_module_disable_rx(module, dai_id);
Vaibhav Agarwal094c4302016-03-29 16:32:37 +0530349 }
Vaibhav Agarwal487dcbd2016-08-04 15:14:33 +0530350
Vaibhav Agarwalaaef32a2016-08-04 15:14:30 +0530351 mutex_unlock(&codec->lock);
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530352
353 return ret;
354}
355EXPORT_SYMBOL(gbaudio_module_update);
356
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700357/*
358 * codec DAI ops
359 */
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +0530360static int gbcodec_startup(struct snd_pcm_substream *substream,
361 struct snd_soc_dai *dai)
362{
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530363 struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev);
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530364 struct gbaudio_stream_params *params;
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700365
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530366 mutex_lock(&codec->lock);
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700367
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530368 if (list_empty(&codec->module_list)) {
369 dev_err(codec->dev, "No codec module available\n");
370 mutex_unlock(&codec->lock);
Vaibhav Agarwal3994e0b2016-01-28 21:15:40 +0530371 return -ENODEV;
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700372 }
373
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530374 params = find_dai_stream_params(codec, dai->id, substream->stream);
375 if (!params) {
376 dev_err(codec->dev, "Failed to fetch dai_stream pointer\n");
377 mutex_unlock(&codec->lock);
378 return -EINVAL;
379 }
380 params->state = GBAUDIO_CODEC_STARTUP;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530381 mutex_unlock(&codec->lock);
Vaibhav Agarwalf2bf63a2016-04-22 11:13:19 -0700382 /* to prevent suspend in case of active audio */
383 pm_stay_awake(dai->dev);
Vaibhav Agarwal25de3492016-01-13 14:07:55 -0700384
Vaibhav Agarwalce941302016-08-04 15:14:29 +0530385 return 0;
Vaibhav Agarwal54e90702016-04-23 20:04:05 +0530386}
387
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +0530388static void gbcodec_shutdown(struct snd_pcm_substream *substream,
389 struct snd_soc_dai *dai)
390{
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530391 struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev);
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530392 struct gbaudio_stream_params *params;
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700393
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530394 mutex_lock(&codec->lock);
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700395
Vaibhav Agarwalce941302016-08-04 15:14:29 +0530396 if (list_empty(&codec->module_list))
397 dev_info(codec->dev, "No codec module available during shutdown\n");
Vaibhav Agarwal29386f02016-02-16 00:27:28 +0530398
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530399 params = find_dai_stream_params(codec, dai->id, substream->stream);
400 if (!params) {
401 dev_err(codec->dev, "Failed to fetch dai_stream pointer\n");
402 mutex_unlock(&codec->lock);
403 return;
404 }
405 params->state = GBAUDIO_CODEC_SHUTDOWN;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530406 mutex_unlock(&codec->lock);
Vaibhav Agarwalf2bf63a2016-04-22 11:13:19 -0700407 pm_relax(dai->dev);
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700408 return;
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +0530409}
410
411static int gbcodec_hw_params(struct snd_pcm_substream *substream,
412 struct snd_pcm_hw_params *hwparams,
413 struct snd_soc_dai *dai)
414{
Vaibhav Agarwal796fad42016-01-28 21:15:39 +0530415 int ret;
Chaehyun Lim31959392016-09-20 09:47:29 +0900416 u8 sig_bits, channels;
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700417 uint32_t format, rate;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530418 struct gbaudio_module_info *module;
419 struct gbaudio_data_connection *data;
Vaibhav Agarwalc388ae72016-08-04 15:14:37 +0530420 struct gb_bundle *bundle;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530421 struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev);
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530422 struct gbaudio_stream_params *params;
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700423
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530424 mutex_lock(&codec->lock);
425
426 if (list_empty(&codec->module_list)) {
427 dev_err(codec->dev, "No codec module available\n");
428 mutex_unlock(&codec->lock);
Vaibhav Agarwal3994e0b2016-01-28 21:15:40 +0530429 return -ENODEV;
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700430 }
431
432 /*
433 * assuming, currently only 48000 Hz, 16BIT_LE, stereo
434 * is supported, validate params before configuring codec
435 */
436 if (params_channels(hwparams) != 2) {
437 dev_err(dai->dev, "Invalid channel count:%d\n",
438 params_channels(hwparams));
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530439 mutex_unlock(&codec->lock);
440 return -EINVAL;
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700441 }
442 channels = params_channels(hwparams);
443
444 if (params_rate(hwparams) != 48000) {
445 dev_err(dai->dev, "Invalid sampling rate:%d\n",
446 params_rate(hwparams));
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530447 mutex_unlock(&codec->lock);
448 return -EINVAL;
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700449 }
450 rate = GB_AUDIO_PCM_RATE_48000;
451
452 if (params_format(hwparams) != SNDRV_PCM_FORMAT_S16_LE) {
453 dev_err(dai->dev, "Invalid format:%d\n",
454 params_format(hwparams));
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530455 mutex_unlock(&codec->lock);
456 return -EINVAL;
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700457 }
458 format = GB_AUDIO_PCM_FMT_S16_LE;
459
Vaibhav Agarwalce941302016-08-04 15:14:29 +0530460 /* find the data connection */
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530461 list_for_each_entry(module, &codec->module_list, list) {
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530462 data = find_data(module, dai->id);
Vaibhav Agarwalce941302016-08-04 15:14:29 +0530463 if (data)
464 break;
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700465 }
Vaibhav Agarwalce941302016-08-04 15:14:29 +0530466
467 if (!data) {
468 dev_err(dai->dev, "DATA connection missing\n");
469 mutex_unlock(&codec->lock);
470 return -EINVAL;
471 }
472
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530473 params = find_dai_stream_params(codec, dai->id, substream->stream);
474 if (!params) {
475 dev_err(codec->dev, "Failed to fetch dai_stream pointer\n");
476 mutex_unlock(&codec->lock);
477 return -EINVAL;
478 }
479
Vaibhav Agarwalc388ae72016-08-04 15:14:37 +0530480 bundle = to_gb_bundle(module->dev);
481 ret = gb_pm_runtime_get_sync(bundle);
482 if (ret) {
483 mutex_unlock(&codec->lock);
484 return ret;
485 }
486
Vaibhav Agarwalce941302016-08-04 15:14:29 +0530487 ret = gb_audio_apbridgea_set_config(data->connection, 0,
488 AUDIO_APBRIDGEA_PCM_FMT_16,
489 AUDIO_APBRIDGEA_PCM_RATE_48000,
490 6144000);
491 if (ret) {
492 dev_err_ratelimited(dai->dev, "%d: Error during set_config\n",
493 ret);
494 mutex_unlock(&codec->lock);
495 return ret;
496 }
Vaibhav Agarwalc388ae72016-08-04 15:14:37 +0530497
498 gb_pm_runtime_put_noidle(bundle);
499
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530500 params->state = GBAUDIO_CODEC_HWPARAMS;
501 params->format = format;
502 params->rate = rate;
503 params->channels = channels;
504 params->sig_bits = sig_bits;
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700505
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530506 mutex_unlock(&codec->lock);
Vaibhav Agarwalce941302016-08-04 15:14:29 +0530507 return 0;
Vaibhav Agarwal54e90702016-04-23 20:04:05 +0530508}
509
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +0530510static int gbcodec_prepare(struct snd_pcm_substream *substream,
511 struct snd_soc_dai *dai)
512{
Vaibhav Agarwal796fad42016-01-28 21:15:39 +0530513 int ret;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530514 struct gbaudio_module_info *module;
Vaibhav Agarwal54e90702016-04-23 20:04:05 +0530515 struct gbaudio_data_connection *data;
Vaibhav Agarwalc388ae72016-08-04 15:14:37 +0530516 struct gb_bundle *bundle;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530517 struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev);
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530518 struct gbaudio_stream_params *params;
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700519
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530520 mutex_lock(&codec->lock);
521
522 if (list_empty(&codec->module_list)) {
523 dev_err(codec->dev, "No codec module available\n");
524 mutex_unlock(&codec->lock);
Vaibhav Agarwal3994e0b2016-01-28 21:15:40 +0530525 return -ENODEV;
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700526 }
527
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530528 list_for_each_entry(module, &codec->module_list, list) {
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530529 /* find the dai */
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530530 data = find_data(module, dai->id);
Vaibhav Agarwalce941302016-08-04 15:14:29 +0530531 if (data)
Vaibhav Agarwal54e90702016-04-23 20:04:05 +0530532 break;
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700533 }
Vaibhav Agarwalce941302016-08-04 15:14:29 +0530534 if (!data) {
535 dev_err(dai->dev, "DATA connection missing\n");
536 mutex_unlock(&codec->lock);
537 return -ENODEV;
538 }
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700539
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530540 params = find_dai_stream_params(codec, dai->id, substream->stream);
541 if (!params) {
542 dev_err(codec->dev, "Failed to fetch dai_stream pointer\n");
543 mutex_unlock(&codec->lock);
544 return -EINVAL;
545 }
546
Vaibhav Agarwalc388ae72016-08-04 15:14:37 +0530547 bundle = to_gb_bundle(module->dev);
548 ret = gb_pm_runtime_get_sync(bundle);
549 if (ret) {
550 mutex_unlock(&codec->lock);
551 return ret;
552 }
553
Vaibhav Agarwalce941302016-08-04 15:14:29 +0530554 switch (substream->stream) {
555 case SNDRV_PCM_STREAM_PLAYBACK:
556 ret = gb_audio_apbridgea_set_tx_data_size(data->connection, 0,
557 192);
558 break;
559 case SNDRV_PCM_STREAM_CAPTURE:
560 ret = gb_audio_apbridgea_set_rx_data_size(data->connection, 0,
561 192);
562 break;
563 }
564 if (ret) {
565 mutex_unlock(&codec->lock);
566 dev_err_ratelimited(dai->dev, "set_data_size failed:%d\n",
567 ret);
568 return ret;
569 }
570
Vaibhav Agarwalc388ae72016-08-04 15:14:37 +0530571 gb_pm_runtime_put_noidle(bundle);
572
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530573 params->state = GBAUDIO_CODEC_PREPARE;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530574 mutex_unlock(&codec->lock);
Vaibhav Agarwalce941302016-08-04 15:14:29 +0530575 return 0;
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700576}
577
Vaibhav Agarwal27c243c2016-06-09 09:30:39 +0530578static int gbcodec_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700579{
Vaibhav Agarwal796fad42016-01-28 21:15:39 +0530580 int ret;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530581 struct gbaudio_data_connection *data;
582 struct gbaudio_module_info *module;
Vaibhav Agarwalc388ae72016-08-04 15:14:37 +0530583 struct gb_bundle *bundle;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530584 struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev);
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530585 struct gbaudio_stream_params *params;
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700586
Vaibhav Agarwal27c243c2016-06-09 09:30:39 +0530587
588 dev_dbg(dai->dev, "Mute:%d, Direction:%s\n", mute,
589 stream ? "CAPTURE":"PLAYBACK");
590
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530591 mutex_lock(&codec->lock);
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530592
593 params = find_dai_stream_params(codec, dai->id, stream);
594 if (!params) {
595 dev_err(codec->dev, "Failed to fetch dai_stream pointer\n");
596 mutex_unlock(&codec->lock);
597 return -EINVAL;
598 }
599
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530600 if (list_empty(&codec->module_list)) {
601 dev_err(codec->dev, "No codec module available\n");
Vaibhav Agarwal12ce5232016-07-12 04:56:00 -0500602 if (mute) {
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530603 params->state = GBAUDIO_CODEC_STOP;
Vaibhav Agarwal12ce5232016-07-12 04:56:00 -0500604 ret = 0;
605 } else {
606 ret = -ENODEV;
607 }
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530608 mutex_unlock(&codec->lock);
Vaibhav Agarwal12ce5232016-07-12 04:56:00 -0500609 return ret;
Vaibhav Agarwal29386f02016-02-16 00:27:28 +0530610 }
Vaibhav Agarwal3994e0b2016-01-28 21:15:40 +0530611
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530612 list_for_each_entry(module, &codec->module_list, list) {
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530613 /* find the dai */
Vaibhav Agarwal60e73272016-08-04 15:14:35 +0530614 data = find_data(module, dai->id);
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530615 if (data)
616 break;
617 }
618 if (!data) {
619 dev_err(dai->dev, "%s:%s DATA connection missing\n",
620 dai->name, module->name);
Vaibhav Agarwal90579d42016-08-04 15:14:28 +0530621 mutex_unlock(&codec->lock);
622 return -ENODEV;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530623 }
Vaibhav Agarwal27c243c2016-06-09 09:30:39 +0530624
Vaibhav Agarwalc388ae72016-08-04 15:14:37 +0530625 bundle = to_gb_bundle(module->dev);
626 ret = gb_pm_runtime_get_sync(bundle);
627 if (ret) {
628 mutex_unlock(&codec->lock);
629 return ret;
630 }
631
Vaibhav Agarwal27c243c2016-06-09 09:30:39 +0530632 if (!mute && !stream) {/* start playback */
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530633 ret = gb_audio_apbridgea_prepare_tx(data->connection,
634 0);
Mark Greer5bbe14b2016-02-29 15:31:02 -0700635 if (!ret)
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530636 ret = gb_audio_apbridgea_start_tx(data->connection,
637 0, 0);
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530638 params->state = GBAUDIO_CODEC_START;
Vaibhav Agarwal27c243c2016-06-09 09:30:39 +0530639 } else if (!mute && stream) {/* start capture */
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530640 ret = gb_audio_apbridgea_prepare_rx(data->connection,
641 0);
642 if (!ret)
643 ret = gb_audio_apbridgea_start_rx(data->connection,
Mark Greer5bbe14b2016-02-29 15:31:02 -0700644 0);
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530645 params->state = GBAUDIO_CODEC_START;
Vaibhav Agarwal27c243c2016-06-09 09:30:39 +0530646 } else if (mute && !stream) {/* stop playback */
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530647 ret = gb_audio_apbridgea_stop_tx(data->connection, 0);
Mark Greer5bbe14b2016-02-29 15:31:02 -0700648 if (!ret)
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530649 ret = gb_audio_apbridgea_shutdown_tx(data->connection,
Mark Greer5bbe14b2016-02-29 15:31:02 -0700650 0);
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530651 params->state = GBAUDIO_CODEC_STOP;
Vaibhav Agarwal27c243c2016-06-09 09:30:39 +0530652 } else if (mute && stream) {/* stop capture */
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530653 ret = gb_audio_apbridgea_stop_rx(data->connection, 0);
Mark Greer5bbe14b2016-02-29 15:31:02 -0700654 if (!ret)
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530655 ret = gb_audio_apbridgea_shutdown_rx(data->connection,
Mark Greer5bbe14b2016-02-29 15:31:02 -0700656 0);
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530657 params->state = GBAUDIO_CODEC_STOP;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530658 } else
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700659 ret = -EINVAL;
Vaibhav Agarwalb7f00882016-01-13 14:07:52 -0700660 if (ret)
Vaibhav Agarwal27c243c2016-06-09 09:30:39 +0530661 dev_err_ratelimited(dai->dev,
662 "%s:Error during %s %s stream:%d\n",
663 module->name, mute ? "Mute" : "Unmute",
664 stream ? "Capture" : "Playback", ret);
Vaibhav Agarwalb07868b2016-02-16 22:16:33 +0530665
Vaibhav Agarwalc388ae72016-08-04 15:14:37 +0530666 gb_pm_runtime_put_noidle(bundle);
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530667 mutex_unlock(&codec->lock);
Vaibhav Agarwal90579d42016-08-04 15:14:28 +0530668 return ret;
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +0530669}
670
671static struct snd_soc_dai_ops gbcodec_dai_ops = {
672 .startup = gbcodec_startup,
673 .shutdown = gbcodec_shutdown,
674 .hw_params = gbcodec_hw_params,
675 .prepare = gbcodec_prepare,
Vaibhav Agarwal27c243c2016-06-09 09:30:39 +0530676 .mute_stream = gbcodec_mute_stream,
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +0530677};
678
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530679static struct snd_soc_dai_driver gbaudio_dai[] = {
680 {
681 .name = "apb-i2s0",
682 .id = 0,
683 .playback = {
684 .stream_name = "I2S 0 Playback",
685 .rates = SNDRV_PCM_RATE_48000,
686 .formats = SNDRV_PCM_FORMAT_S16_LE,
687 .rate_max = 48000,
688 .rate_min = 48000,
689 .channels_min = 1,
690 .channels_max = 2,
691 },
692 .capture = {
693 .stream_name = "I2S 0 Capture",
694 .rates = SNDRV_PCM_RATE_48000,
695 .formats = SNDRV_PCM_FORMAT_S16_LE,
696 .rate_max = 48000,
697 .rate_min = 48000,
698 .channels_min = 1,
699 .channels_max = 2,
700 },
701 .ops = &gbcodec_dai_ops,
702 },
703};
704
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +0530705static int gbaudio_init_jack(struct gbaudio_module_info *module,
706 struct snd_soc_codec *codec)
707{
708 int ret;
709
Vaibhav Agarwal847175e82016-09-01 11:38:40 +0530710 if (!module->jack_mask)
711 return 0;
712
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +0530713 snprintf(module->jack_name, NAME_SIZE, "GB %d Headset Jack",
714 module->dev_id);
Vaibhav Agarwal847175e82016-09-01 11:38:40 +0530715 ret = snd_soc_jack_new(codec, module->jack_name, module->jack_mask,
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +0530716 &module->headset_jack);
717 if (ret) {
718 dev_err(module->dev, "Failed to create new jack\n");
719 return ret;
720 }
721
Vaibhav Agarwal847175e82016-09-01 11:38:40 +0530722 if (!module->button_mask)
723 return 0;
724
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +0530725 snprintf(module->button_name, NAME_SIZE, "GB %d Button Jack",
726 module->dev_id);
Vaibhav Agarwal847175e82016-09-01 11:38:40 +0530727 ret = snd_soc_jack_new(codec, module->button_name, module->button_mask,
728 &module->button_jack);
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +0530729 if (ret) {
730 dev_err(module->dev, "Failed to create button jack\n");
731 return ret;
732 }
733
Vaibhav Agarwal847175e82016-09-01 11:38:40 +0530734 /*
735 * Currently, max 4 buttons are supported with following key mapping
736 * BTN_0 = KEY_MEDIA
737 * BTN_1 = KEY_VOICECOMMAND
738 * BTN_2 = KEY_VOLUMEUP
739 * BTN_3 = KEY_VOLUMEDOWN
740 */
741
742 if (module->button_mask & SND_JACK_BTN_0) {
743 ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_0,
744 KEY_MEDIA);
745 if (ret) {
746 dev_err(module->dev, "Failed to set BTN_0\n");
747 return ret;
748 }
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +0530749 }
750
Vaibhav Agarwal847175e82016-09-01 11:38:40 +0530751 if (module->button_mask & SND_JACK_BTN_1) {
752 ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_1,
753 KEY_VOICECOMMAND);
754 if (ret) {
755 dev_err(module->dev, "Failed to set BTN_1\n");
756 return ret;
757 }
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +0530758 }
759
Vaibhav Agarwal847175e82016-09-01 11:38:40 +0530760 if (module->button_mask & SND_JACK_BTN_2) {
761 ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_2,
762 KEY_VOLUMEUP);
763 if (ret) {
764 dev_err(module->dev, "Failed to set BTN_2\n");
765 return ret;
766 }
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +0530767 }
768
Vaibhav Agarwal847175e82016-09-01 11:38:40 +0530769 if (module->button_mask & SND_JACK_BTN_3) {
770 ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_3,
771 KEY_VOLUMEDOWN);
772 if (ret) {
773 dev_err(module->dev, "Failed to set BTN_0\n");
774 return ret;
775 }
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +0530776 }
777
778 /* FIXME
779 * verify if this is really required
780 set_bit(INPUT_PROP_NO_DUMMY_RELEASE,
781 module->button_jack.jack->input_dev->propbit);
782 */
783
784 return 0;
785}
786
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530787int gbaudio_register_module(struct gbaudio_module_info *module)
788{
789 int ret;
790 struct snd_soc_codec *codec;
Vaibhav Agarwal2b8c2b52016-04-21 22:14:02 +0530791 struct snd_card *card;
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +0530792 struct snd_soc_jack *jack = NULL;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530793
794 if (!gbcodec) {
795 dev_err(module->dev, "GB Codec not yet probed\n");
796 return -EAGAIN;
797 }
798
799 codec = gbcodec->codec;
Vaibhav Agarwal2b8c2b52016-04-21 22:14:02 +0530800 card = codec->card->snd_card;
801
802 down_write(&card->controls_rwsem);
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530803
804 if (module->num_dais) {
805 dev_err(gbcodec->dev,
806 "%d:DAIs not supported via gbcodec driver\n",
807 module->num_dais);
Vaibhav Agarwal2b8c2b52016-04-21 22:14:02 +0530808 up_write(&card->controls_rwsem);
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530809 return -EINVAL;
810 }
811
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +0530812 ret = gbaudio_init_jack(module, codec);
813 if (ret) {
Vaibhav Agarwal2b8c2b52016-04-21 22:14:02 +0530814 up_write(&card->controls_rwsem);
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +0530815 return ret;
816 }
817
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530818 if (module->dapm_widgets)
819 snd_soc_dapm_new_controls(&codec->dapm, module->dapm_widgets,
820 module->num_dapm_widgets);
821 if (module->controls)
822 snd_soc_add_codec_controls(codec, module->controls,
823 module->num_controls);
824 if (module->dapm_routes)
825 snd_soc_dapm_add_routes(&codec->dapm, module->dapm_routes,
826 module->num_dapm_routes);
827
828 /* card already instantiated, create widgets here only */
829 if (codec->card->instantiated) {
Vaibhav Agarwalbb9986e2016-05-04 16:29:22 +0530830 snd_soc_dapm_link_component_dai_widgets(codec->card,
831 &codec->dapm);
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +0530832#ifdef CONFIG_SND_JACK
Vaibhav Agarwalbb9986e2016-05-04 16:29:22 +0530833 /* register jack devices for this module from codec->jack_list */
834 list_for_each_entry(jack, &codec->jack_list, list) {
835 if ((jack == &module->headset_jack)
836 || (jack == &module->button_jack))
837 snd_device_register(codec->card->snd_card,
838 jack->jack);
839 }
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +0530840#endif
Vaibhav Agarwalbb9986e2016-05-04 16:29:22 +0530841 }
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +0530842
Vaibhav Agarwalc188fdc2016-05-04 16:29:23 +0530843 mutex_lock(&gbcodec->lock);
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530844 list_add(&module->list, &gbcodec->module_list);
Vaibhav Agarwalc188fdc2016-05-04 16:29:23 +0530845 mutex_unlock(&gbcodec->lock);
846
Vaibhav Agarwalbb9986e2016-05-04 16:29:22 +0530847 if (codec->card->instantiated)
848 ret = snd_soc_dapm_new_widgets(&codec->dapm);
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530849 dev_dbg(codec->dev, "Registered %s module\n", module->name);
850
Vaibhav Agarwal2b8c2b52016-04-21 22:14:02 +0530851 up_write(&card->controls_rwsem);
Vaibhav Agarwalbb9986e2016-05-04 16:29:22 +0530852 return ret;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530853}
854EXPORT_SYMBOL(gbaudio_register_module);
855
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530856static void gbaudio_codec_clean_data_tx(struct gbaudio_data_connection *data)
857{
Chaehyun Lim79cb2b262016-09-20 09:47:30 +0900858 u16 i2s_port, cportid;
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530859 int ret;
860
861 if (list_is_singular(&gbcodec->module_list)) {
862 ret = gb_audio_apbridgea_stop_tx(data->connection, 0);
863 if (ret)
864 return;
865 ret = gb_audio_apbridgea_shutdown_tx(data->connection,
866 0);
867 if (ret)
868 return;
869 }
870 i2s_port = 0; /* fixed for now */
871 cportid = data->connection->hd_cport_id;
872 ret = gb_audio_apbridgea_unregister_cport(data->connection,
873 i2s_port, cportid,
874 AUDIO_APBRIDGEA_DIRECTION_TX);
875 data->state[0] = GBAUDIO_CODEC_SHUTDOWN;
876}
877
878static void gbaudio_codec_clean_data_rx(struct gbaudio_data_connection *data)
879{
Chaehyun Lim79cb2b262016-09-20 09:47:30 +0900880 u16 i2s_port, cportid;
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530881 int ret;
882
883 if (list_is_singular(&gbcodec->module_list)) {
884 ret = gb_audio_apbridgea_stop_rx(data->connection, 0);
885 if (ret)
886 return;
887 ret = gb_audio_apbridgea_shutdown_rx(data->connection,
888 0);
889 if (ret)
890 return;
891 }
892 i2s_port = 0; /* fixed for now */
893 cportid = data->connection->hd_cport_id;
894 ret = gb_audio_apbridgea_unregister_cport(data->connection,
895 i2s_port, cportid,
896 AUDIO_APBRIDGEA_DIRECTION_RX);
897 data->state[1] = GBAUDIO_CODEC_SHUTDOWN;
898}
899
900
Viresh Kumar36460e82016-04-21 08:11:57 +0530901static void gbaudio_codec_cleanup(struct gbaudio_module_info *module)
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530902{
903 struct gbaudio_data_connection *data;
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530904 int pb_state, cap_state;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530905
Vaibhav Agarwald7642122016-03-29 23:31:43 +0530906 dev_dbg(gbcodec->dev, "%s: removed, cleanup APBridge\n", module->name);
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530907 list_for_each_entry(data, &module->data_list, list) {
908 pb_state = data->state[0];
909 cap_state = data->state[1];
Vaibhav Agarwald7642122016-03-29 23:31:43 +0530910
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530911 if (pb_state > GBAUDIO_CODEC_SHUTDOWN)
912 gbaudio_codec_clean_data_tx(data);
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530913
Vaibhav Agarwal591c4522016-08-04 15:14:39 +0530914 if (cap_state > GBAUDIO_CODEC_SHUTDOWN)
915 gbaudio_codec_clean_data_rx(data);
916
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530917 }
918}
919
920void gbaudio_unregister_module(struct gbaudio_module_info *module)
921{
922 struct snd_soc_codec *codec = gbcodec->codec;
923 struct snd_card *card = codec->card->snd_card;
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +0530924 struct snd_soc_jack *jack, *next_j;
Vaibhav Agarwal33cc2832016-08-05 18:16:30 +0530925 int mask;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530926
927 dev_dbg(codec->dev, "Unregister %s module\n", module->name);
928
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530929 down_write(&card->controls_rwsem);
930 mutex_lock(&gbcodec->lock);
Vaibhav Agarwalbb9986e2016-05-04 16:29:22 +0530931 gbaudio_codec_cleanup(module);
932 list_del(&module->list);
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530933 dev_dbg(codec->dev, "Process Unregister %s module\n", module->name);
Vaibhav Agarwalc188fdc2016-05-04 16:29:23 +0530934 mutex_unlock(&gbcodec->lock);
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530935
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +0530936#ifdef CONFIG_SND_JACK
937 /* free jack devices for this module from codec->jack_list */
938 list_for_each_entry_safe(jack, next_j, &codec->jack_list, list) {
Vaibhav Agarwal33cc2832016-08-05 18:16:30 +0530939 if (jack == &module->headset_jack)
940 mask = GBCODEC_JACK_MASK;
941 else if (jack == &module->button_jack)
942 mask = GBCODEC_JACK_BUTTON_MASK;
943 else
944 mask = 0;
945 if (mask) {
946 dev_dbg(module->dev, "Report %s removal\n",
947 jack->jack->id);
948 snd_soc_jack_report(jack, 0, mask);
Vaibhav Agarwal64a7e2c2016-03-29 16:32:36 +0530949 snd_device_free(codec->card->snd_card, jack->jack);
950 list_del(&jack->list);
951 }
952 }
953#endif
954
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530955 if (module->dapm_routes) {
956 dev_dbg(codec->dev, "Removing %d routes\n",
957 module->num_dapm_routes);
958 snd_soc_dapm_del_routes(&codec->dapm, module->dapm_routes,
959 module->num_dapm_routes);
960 }
961 if (module->controls) {
962 dev_dbg(codec->dev, "Removing %d controls\n",
963 module->num_controls);
Vaibhav Agarwal2b8c2b52016-04-21 22:14:02 +0530964 snd_soc_remove_codec_controls(codec, module->controls,
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530965 module->num_controls);
966 }
967 if (module->dapm_widgets) {
968 dev_dbg(codec->dev, "Removing %d widgets\n",
969 module->num_dapm_widgets);
970 snd_soc_dapm_free_controls(&codec->dapm, module->dapm_widgets,
971 module->num_dapm_widgets);
972 }
973
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530974 dev_dbg(codec->dev, "Unregistered %s module\n", module->name);
975
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530976 up_write(&card->controls_rwsem);
977}
978EXPORT_SYMBOL(gbaudio_unregister_module);
979
Vaibhav Agarwal6339d232016-01-13 14:07:51 -0700980/*
981 * codec driver ops
982 */
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +0530983static int gbcodec_probe(struct snd_soc_codec *codec)
984{
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530985 int i;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530986 struct gbaudio_codec_info *info;
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530987 struct gbaudio_codec_dai *dai;
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +0530988
989 info = devm_kzalloc(codec->dev, sizeof(*info), GFP_KERNEL);
990 if (!info)
991 return -ENOMEM;
992
993 info->dev = codec->dev;
994 INIT_LIST_HEAD(&info->module_list);
995 mutex_init(&info->lock);
Vaibhav Agarwal19866602016-08-04 15:14:38 +0530996 INIT_LIST_HEAD(&info->dai_list);
997
998 /* init dai_list used to maintain runtime stream info */
999 for (i = 0; i < ARRAY_SIZE(gbaudio_dai); i++) {
1000 dai = devm_kzalloc(codec->dev, sizeof(*dai), GFP_KERNEL);
1001 if (!dai)
1002 return -ENOMEM;
1003 dai->id = gbaudio_dai[i].id;
1004 list_add(&dai->list, &info->dai_list);
1005 }
1006
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +05301007 info->codec = codec;
1008 snd_soc_codec_set_drvdata(codec, info);
1009 gbcodec = info;
1010
Richard Groux33111572016-09-21 19:05:31 +02001011 device_init_wakeup(codec->dev, 1);
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +05301012 return 0;
1013}
1014
1015static int gbcodec_remove(struct snd_soc_codec *codec)
1016{
1017 /* Empty function for now */
1018 return 0;
1019}
1020
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +05301021static u8 gbcodec_reg[GBCODEC_REG_COUNT] = {
1022 [GBCODEC_CTL_REG] = GBCODEC_CTL_REG_DEFAULT,
1023 [GBCODEC_MUTE_REG] = GBCODEC_MUTE_REG_DEFAULT,
1024 [GBCODEC_PB_LVOL_REG] = GBCODEC_PB_VOL_REG_DEFAULT,
1025 [GBCODEC_PB_RVOL_REG] = GBCODEC_PB_VOL_REG_DEFAULT,
1026 [GBCODEC_CAP_LVOL_REG] = GBCODEC_CAP_VOL_REG_DEFAULT,
1027 [GBCODEC_CAP_RVOL_REG] = GBCODEC_CAP_VOL_REG_DEFAULT,
1028 [GBCODEC_APB1_MUX_REG] = GBCODEC_APB1_MUX_REG_DEFAULT,
1029 [GBCODEC_APB2_MUX_REG] = GBCODEC_APB2_MUX_REG_DEFAULT,
1030};
1031
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +05301032static int gbcodec_write(struct snd_soc_codec *codec, unsigned int reg,
1033 unsigned int value)
1034{
1035 int ret = 0;
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +05301036
1037 if (reg == SND_SOC_NOPM)
1038 return 0;
1039
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +05301040 BUG_ON(reg >= GBCODEC_REG_COUNT);
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +05301041
1042 gbcodec_reg[reg] = value;
1043 dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, value);
1044
1045 return ret;
1046}
1047
1048static unsigned int gbcodec_read(struct snd_soc_codec *codec,
1049 unsigned int reg)
1050{
1051 unsigned int val = 0;
1052
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +05301053 if (reg == SND_SOC_NOPM)
1054 return 0;
1055
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +05301056 BUG_ON(reg >= GBCODEC_REG_COUNT);
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +05301057
1058 val = gbcodec_reg[reg];
1059 dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, val);
1060
1061 return val;
1062}
1063
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +05301064static struct snd_soc_codec_driver soc_codec_dev_gbaudio = {
1065 .probe = gbcodec_probe,
1066 .remove = gbcodec_remove,
1067
1068 .read = gbcodec_read,
1069 .write = gbcodec_write,
1070
1071 .reg_cache_size = GBCODEC_REG_COUNT,
1072 .reg_cache_default = gbcodec_reg_defaults,
1073 .reg_word_size = 1,
1074
1075 .idle_bias_off = true,
1076 .ignore_pmdown_time = 1,
Viresh Kumar35e28792016-01-27 16:57:48 +05301077};
Vaibhav Agarwal2a70e492016-01-13 14:07:50 -07001078
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +05301079#ifdef CONFIG_PM
1080static int gbaudio_codec_suspend(struct device *dev)
1081{
1082 dev_dbg(dev, "%s: suspend\n", __func__);
1083 return 0;
1084}
1085
1086static int gbaudio_codec_resume(struct device *dev)
1087{
1088 dev_dbg(dev, "%s: resume\n", __func__);
1089 return 0;
1090}
1091
1092static const struct dev_pm_ops gbaudio_codec_pm_ops = {
1093 .suspend = gbaudio_codec_suspend,
1094 .resume = gbaudio_codec_resume,
1095};
1096#endif
1097
1098static int gbaudio_codec_probe(struct platform_device *pdev)
1099{
1100 return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_gbaudio,
1101 gbaudio_dai, ARRAY_SIZE(gbaudio_dai));
1102}
1103
1104static int gbaudio_codec_remove(struct platform_device *pdev)
1105{
1106 snd_soc_unregister_codec(&pdev->dev);
1107 return 0;
1108}
1109
1110static const struct of_device_id greybus_asoc_machine_of_match[] = {
Vaibhav Agarwal098dfaf2016-07-22 09:41:30 +05301111 { .compatible = "toshiba,apb-dummy-codec", },
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +05301112 {},
1113};
1114
1115static struct platform_driver gbaudio_codec_driver = {
1116 .driver = {
Vaibhav Agarwal098dfaf2016-07-22 09:41:30 +05301117 .name = "apb-dummy-codec",
Vaibhav Agarwal6dd67642016-03-29 23:31:41 +05301118 .owner = THIS_MODULE,
1119#ifdef CONFIG_PM
1120 .pm = &gbaudio_codec_pm_ops,
1121#endif
1122 .of_match_table = greybus_asoc_machine_of_match,
1123 },
1124 .probe = gbaudio_codec_probe,
1125 .remove = gbaudio_codec_remove,
1126};
1127module_platform_driver(gbaudio_codec_driver);
1128
Vaibhav Agarwal098dfaf2016-07-22 09:41:30 +05301129MODULE_DESCRIPTION("APBridge ALSA SoC dummy codec driver");
Vaibhav Agarwald3d2af52015-11-23 15:57:45 +05301130MODULE_AUTHOR("Vaibhav Agarwal <vaibhav.agarwal@linaro.org>");
1131MODULE_LICENSE("GPL v2");
Vaibhav Agarwal098dfaf2016-07-22 09:41:30 +05301132MODULE_ALIAS("platform:apb-dummy-codec");