1 /*
2 * Copyright (C) 2021–2022 Beijing OSWare Technology Co., Ltd
3 * This file contains confidential and proprietary information of
4 * OSWare Technology Co., Ltd
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 #include <sound/soc.h>
20 #include <sound/jack.h>
21 #include <sound/control.h>
22 #include <sound/pcm_params.h>
23 #include <sound/soc-dapm.h>
24 #include <linux/clk.h>
25 #include <linux/clk/clk-conf.h>
26 #include <linux/module.h>
27 #include <linux/of_platform.h>
28 #include <linux/i2c.h>
29 #include <linux/of_gpio.h>
30 #include <linux/slab.h>
31 #include <linux/gpio.h>
32 #include <linux/pinctrl/consumer.h>
33 #include <linux/mfd/syscon.h>
34
35 #include "imx8mm_common.h"
36 #include "audio_core.h"
37 #include "audio_driver_log.h"
38 #include "osal_io.h"
39 #include "audio_dai_base.h"
40 #include "audio_platform_base.h"
41 #include "imx8mm_platform_ops.h"
42 #include "dma_driver.h"
43
44 #define HDF_LOG_TAG imx8mm_dai_adapter
45
46 #define DAI_FIND_NEXT 0
47 #define DAI_FIND_SUCCESS 2
48
49 #define AUDIO_DRV_PCM_IOCTL_RENDER_START 5
50 #define AUDIO_DRV_PCM_IOCTL_RENDER_STOP 6
51 #define AUDIO_DRV_PCM_IOCTL_CAPTURE_START 7
52 #define AUDIO_DRV_PCM_IOCTL_CAPTURE_STOP 8
53 #define AUDIO_DRV_PCM_IOCTL_RENDER_PAUSE 9
54 #define AUDIO_DRV_PCM_IOCTL_CAPTURE_PAUSE 10
55 #define AUDIO_DRV_PCM_IOCTL_RENDER_RESUME 11
56 #define AUDIO_DRV_PCM_IOCTL_CAPTURE_RESUME 12
57
58 #define TRIGGER_TX (0)
59 #define TRIGGER_RX (1)
60 #define TRIGGER_START (0)
61 #define TRIGGER_STOP (1)
62
63 struct PrivDaiData {
64 struct platform_device *pdev;
65 const char *dai_dev_name;
66 struct clk *mclk;
67 struct regmap *gpr;
68 };
69
70 extern int32_t DaiHwParams(const struct AudioCard *card, const struct AudioPcmHwParams *param);
71 extern int32_t DaiTrigger(const struct AudioCard *card, int cmd, const struct DaiDevice *device);
72 extern int32_t DaiDeviceInit(struct AudioCard *audioCard, const struct DaiDevice *dai);
73 extern int32_t SaiTrigger(const struct DaiData *pd, int cmd, int isTx);
74 extern int32_t SaiRuntimeSuspend(const struct DaiData *daiData);
75 extern int32_t SaiHwFree(const struct DaiData *pd, int isTx);
76
77 struct AudioDaiOps g_dai_device_ops = {
78 .HwParams = DaiHwParams,
79 .Trigger = DaiTrigger,
80 };
81
82 struct DaiData g_dai_data = {
83 .DaiInit = DaiDeviceInit,
84 .ops = &g_dai_device_ops,
85 };
86
87
DaiDriverBind(struct HdfDeviceObject * device)88 static int32_t DaiDriverBind(struct HdfDeviceObject *device)
89 {
90 struct DaiHost *daiHost = NULL;
91 AUDIO_DRIVER_LOG_DEBUG("entry!");
92
93 if (device == NULL) {
94 AUDIO_DRIVER_LOG_ERR("input para is NULL.");
95 return HDF_FAILURE;
96 }
97
98 daiHost = (struct DaiHost *)OsalMemCalloc(sizeof(*daiHost));
99 if (daiHost == NULL) {
100 AUDIO_DRIVER_LOG_ERR("malloc host fail!");
101 return HDF_FAILURE;
102 }
103
104 daiHost->device = device;
105 g_dai_data.daiInitFlag = false;
106 device->service = &daiHost->service;
107
108 AUDIO_DRIVER_LOG_ERR("success!");
109 return HDF_SUCCESS;
110 }
111
DaiDeviceInit(struct AudioCard * audioCard,const struct DaiDevice * dai)112 int32_t DaiDeviceInit(struct AudioCard *audioCard, const struct DaiDevice *dai)
113 {
114 struct DaiHost *daiHost = NULL;
115
116 if (dai == NULL || dai->device == NULL || dai->devDaiName == NULL) {
117 AUDIO_DRIVER_LOG_ERR("dai is nullptr.");
118 return HDF_FAILURE;
119 }
120
121 AUDIO_DRIVER_LOG_ERR("cpu dai device name: %s\n", dai->devDaiName);
122 (void)audioCard;
123
124 daiHost = (struct DaiHost *)dai->device->service;
125 if (daiHost == NULL) {
126 AUDIO_DRIVER_LOG_ERR("dai host is NULL.");
127 return HDF_FAILURE;
128 }
129
130 if (g_dai_data.daiInitFlag == true) {
131 AUDIO_DRIVER_LOG_DEBUG("dai init complete!");
132 return HDF_SUCCESS;
133 }
134
135 g_dai_data.daiInitFlag = true;
136
137 return HDF_SUCCESS;
138 }
139
CheckSampleRate(unsigned int sampleRates)140 static int32_t CheckSampleRate(unsigned int sampleRates)
141 {
142 bool checkSampleRate = (sampleRates == AUDIO_SAMPLE_RATE_8000 ||
143 sampleRates == AUDIO_SAMPLE_RATE_12000 ||
144 sampleRates == AUDIO_SAMPLE_RATE_11025 ||
145 sampleRates == AUDIO_SAMPLE_RATE_16000 ||
146 sampleRates == AUDIO_SAMPLE_RATE_22050 ||
147 sampleRates == AUDIO_SAMPLE_RATE_24000 ||
148 sampleRates == AUDIO_SAMPLE_RATE_32000 ||
149 sampleRates == AUDIO_SAMPLE_RATE_44100 ||
150 sampleRates == AUDIO_SAMPLE_RATE_48000 ||
151 sampleRates == AUDIO_SAMPLE_RATE_64000 ||
152 sampleRates == AUDIO_SAMPLE_RATE_96000);
153 if (checkSampleRate) {
154 return HDF_SUCCESS;
155 } else {
156 AUDIO_DRIVER_LOG_ERR("FramatToSampleRate fail: Invalid sampling rate: %d.", sampleRates);
157 return HDF_ERR_NOT_SUPPORT;
158 }
159 }
160
DaiHwParams(const struct AudioCard * card,const struct AudioPcmHwParams * param)161 int32_t DaiHwParams(const struct AudioCard *card, const struct AudioPcmHwParams *param)
162 {
163 struct DaiHost *daiHost = NULL;
164 struct PrivDaiData *pdd = NULL;
165 int ret = 0;
166
167 AUDIO_DRIVER_LOG_DEBUG("entry.");
168
169 if (card == NULL || card->rtd == NULL || card->rtd->platform == NULL ||
170 param == NULL || param->cardServiceName == NULL) {
171 AUDIO_DRIVER_LOG_ERR("input para is nullptr.");
172 return HDF_FAILURE;
173 }
174
175 ret = CheckSampleRate(param->rate);
176 if (ret != HDF_SUCCESS) {
177 AUDIO_DRIVER_LOG_ERR("CheckSampleRate: fail.");
178 return HDF_ERR_NOT_SUPPORT;
179 }
180
181 daiHost = (struct DaiHost *)card->rtd->cpuDai->device->service;
182 if (daiHost == NULL) {
183 AUDIO_DRIVER_LOG_ERR("platformHost is nullptr.");
184 return HDF_FAILURE;
185 }
186
187 pdd = (struct PrivDaiData *)daiHost->priv;
188 if (pdd == NULL) {
189 AUDIO_DRIVER_LOG_ERR("daiHost is null");
190 return HDF_FAILURE;
191 }
192
193 if (pdd->mclk) {
194 AUDIO_DRIVER_LOG_ERR("enable mclk");
195 } else {
196 AUDIO_DRIVER_LOG_ERR("daihost mclk nullptr");
197 return HDF_FAILURE;
198 }
199
200 if (ret) {
201 AUDIO_DRIVER_LOG_ERR("mclk enable failed");
202 return HDF_FAILURE;
203 }
204
205 struct DaiData *data = DaiDataFromCard(card);
206 if (data == NULL) {
207 AUDIO_DRIVER_LOG_ERR("platformHost is nullptr.");
208 return HDF_FAILURE;
209 }
210
211 data->pcmInfo.channels = param->channels;
212 data->pcmInfo.rate = param->rate;
213 data->pcmInfo.streamType = param->streamType;
214
215 return HDF_SUCCESS;
216 }
217
DaiTriggerDMAInit(struct PlatformData * pData)218 static int DaiTriggerDMAInit(struct PlatformData *pData)
219 {
220 int32_t ret = 0;
221
222 if (pData == NULL) {
223 return HDF_FAILURE;
224 }
225
226 if (pData->renderBufInfo.virtAddr == NULL) {
227 ret = DMAInitTxBuff(pData);
228 if (ret != HDF_SUCCESS) {
229 AUDIO_DRIVER_LOG_ERR("DMAAoInit: fail");
230 return HDF_FAILURE;
231 }
232 ret = DMAConfigTxBuff(pData);
233 if (ret != HDF_SUCCESS) {
234 AUDIO_DRIVER_LOG_ERR("DMAAoInit: fail");
235 return HDF_FAILURE;
236 }
237 }
238
239 return ret;
240 }
241
242
243 extern int32_t g_dmaRequestChannel;
DaiTrigger(const struct AudioCard * card,int cmd,const struct DaiDevice * device)244 int32_t DaiTrigger(const struct AudioCard *card, int cmd, const struct DaiDevice *device)
245 {
246 struct DaiData *data = device->devData;
247 bool tx = data->pcmInfo.streamType == AUDIO_CAPTURE_STREAM;
248 int32_t ret = 0;
249 struct PlatformData *pData = PlatformDataFromCard(card);
250
251 switch (cmd) {
252 case AUDIO_DRV_PCM_IOCTL_RENDER_START:
253 case AUDIO_DRV_PCM_IOCTL_CAPTURE_START:
254 case AUDIO_DRV_PCM_IOCTL_RENDER_RESUME:
255 case AUDIO_DRV_PCM_IOCTL_CAPTURE_RESUME:
256 if (g_dmaRequestChannel == 0) {
257 Imx8mmDmaRequestChannel(pData, data->pcmInfo.streamType);
258
259 DaiTriggerDMAInit(pData);
260
261 ret = DMAEnableTx(pData);
262 if (ret != HDF_SUCCESS) {
263 AUDIO_DRIVER_LOG_ERR("DMAEnableTx failed");
264 return HDF_FAILURE;
265 }
266 }
267
268 g_dmaRequestChannel = 0;
269 ret = SaiTrigger(data, TRIGGER_START, tx);
270 if (ret != HDF_SUCCESS) {
271 AUDIO_DRIVER_LOG_ERR("SaiTrigger failed");
272 return HDF_FAILURE;
273 }
274 break;
275 case AUDIO_DRV_PCM_IOCTL_RENDER_STOP:
276 case AUDIO_DRV_PCM_IOCTL_CAPTURE_STOP:
277 case AUDIO_DRV_PCM_IOCTL_RENDER_PAUSE:
278 case AUDIO_DRV_PCM_IOCTL_CAPTURE_PAUSE:
279 ret = SaiTrigger(data, TRIGGER_STOP, tx);
280 if (ret != HDF_SUCCESS) {
281 AUDIO_DRIVER_LOG_ERR("SaiTrigger failed");
282 return HDF_FAILURE;
283 }
284
285 ret = SaiRuntimeSuspend(data);
286 if (ret != HDF_SUCCESS) {
287 AUDIO_DRIVER_LOG_ERR("runtime suspend failed");
288 return HDF_FAILURE;
289 }
290 break;
291 default:
292 break;
293 }
294 return HDF_SUCCESS;
295 }
296
DaiGetInfoFromHcs(struct PrivDaiData * p_data,const struct DeviceResourceNode * node)297 static int32_t DaiGetInfoFromHcs(struct PrivDaiData *p_data, const struct DeviceResourceNode *node)
298 {
299 struct DeviceResourceIface *iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
300 if (p_data == NULL || node == NULL || iface == NULL || iface->GetString == NULL) {
301 AUDIO_DRIVER_LOG_ERR("%s: face is invalid", __func__);
302 return HDF_FAILURE;
303 }
304
305 if (iface->GetString(node, "dai_dev_name", &p_data->dai_dev_name, NULL) != HDF_SUCCESS) {
306 AUDIO_DRIVER_LOG_ERR("%s: read dai name fail", __func__);
307 return HDF_FAILURE;
308 }
309
310 AUDIO_DRIVER_LOG_ERR("%s: dai_name = %s", __func__, p_data->dai_dev_name);
311
312 return HDF_SUCCESS;
313 }
314
DaiFindDeviceFromBus(struct device * dev,void * para)315 static int32_t DaiFindDeviceFromBus(struct device *dev, void *para)
316 {
317 struct platform_device *pdev = NULL;
318 struct PrivDaiData *pdd = (struct PrivDaiData*)para;
319
320 if (dev == NULL || para == NULL) {
321 AUDIO_DRIVER_LOG_ERR("invalid param.\n");
322 return HDF_ERR_INVALID_PARAM;
323 }
324
325 pdev = to_platform_device(dev);
326 if (pdev->name == NULL) {
327 AUDIO_DRIVER_LOG_ERR("pdev name NULL\n");
328 return HDF_ERR_INVALID_PARAM;
329 }
330
331 if (!strstr(pdev->name, pdd->dai_dev_name)) {
332 return DAI_FIND_NEXT;
333 }
334
335 pdd->pdev = pdev;
336
337 return DAI_FIND_SUCCESS;
338 }
339
340 #define MCLK_BITS (4)
341 #define MCLK_BITS_OFFSET (20)
DaiInit(struct DaiHost * daiHost,struct HdfDeviceObject * device)342 static int32_t DaiInit(struct DaiHost * daiHost, struct HdfDeviceObject *device)
343 {
344 int ret = 0;
345 struct PrivDaiData *pdd;
346 struct device_node *codec_np = NULL, *gpr_np;
347 struct i2c_client *codec_dev;
348
349 if (device->property == NULL || daiHost == NULL) {
350 return HDF_FAILURE;
351 }
352
353 pdd = (struct PrivDaiData *)OsalMemCalloc(sizeof(struct PrivDaiData));
354 if (pdd == NULL) {
355 return HDF_ERR_MALLOC_FAIL;
356 }
357
358 ret = DaiGetInfoFromHcs(pdd, device->property);
359 if (ret != HDF_SUCCESS) {
360 return HDF_FAILURE;
361 }
362
363 daiHost->priv = pdd;
364
365 ret = bus_for_each_dev(&platform_bus_type, NULL, (void*)pdd, DaiFindDeviceFromBus);
366 if (ret != DAI_FIND_SUCCESS) {
367 return HDF_FAILURE;
368 }
369
370 ret = of_clk_set_defaults(pdd->pdev->dev.of_node, false);
371 if (ret < 0) {
372 return HDF_FAILURE;
373 }
374
375 codec_np = of_parse_phandle(pdd->pdev->dev.of_node, "audio-codec", 0);
376 if (!codec_np) {
377 return HDF_FAILURE;
378 }
379
380 codec_dev = of_find_i2c_device_by_node(codec_np);
381 if (!codec_dev) {
382 of_node_put(codec_np);
383 return HDF_FAILURE;
384 }
385
386 pdd->mclk = devm_clk_get(&codec_dev->dev, "mclk");
387 if (IS_ERR(pdd->mclk)) {
388 of_node_put(codec_np);
389 return HDF_FAILURE;
390 }
391
392 gpr_np = of_parse_phandle(pdd->pdev->dev.of_node, "gpr", 0);
393 if (gpr_np) {
394 pdd->gpr = syscon_node_to_regmap(gpr_np);
395 if (IS_ERR(pdd->gpr)) {
396 if (codec_np) {
397 of_node_put(codec_np);
398 }
399
400 return HDF_FAILURE;
401 }
402
403 /* set SAI2_MCLK_DIR to enable codec MCLK for imx7d */
404 regmap_update_bits(pdd->gpr, MCLK_BITS, 1 << MCLK_BITS_OFFSET, 1 << MCLK_BITS_OFFSET);
405 }
406
407 return HDF_SUCCESS;
408 }
409
DaiGetServiceName(const struct HdfDeviceObject * device)410 static int32_t DaiGetServiceName(const struct HdfDeviceObject *device)
411 {
412 const struct DeviceResourceNode *node = NULL;
413 struct DeviceResourceIface *drsOps = NULL;
414 int32_t ret;
415
416 if (device == NULL) {
417 AUDIO_DRIVER_LOG_ERR("input para is nullptr.");
418 return HDF_FAILURE;
419 }
420
421 node = device->property;
422 if (node == NULL) {
423 AUDIO_DRIVER_LOG_ERR("drs node is nullptr.");
424 return HDF_FAILURE;
425 }
426 drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
427 if (drsOps == NULL || drsOps->GetString == NULL) {
428 AUDIO_DRIVER_LOG_ERR("invalid drs ops fail!");
429 return HDF_FAILURE;
430 }
431
432 ret = drsOps->GetString(node, "serviceName", &g_dai_data.drvDaiName, 0);
433 if (ret != HDF_SUCCESS) {
434 AUDIO_DRIVER_LOG_ERR("read serviceName fail!");
435 return ret;
436 }
437
438 return HDF_SUCCESS;
439 }
440
DaiDriverInit(struct HdfDeviceObject * device)441 static int32_t DaiDriverInit(struct HdfDeviceObject *device)
442 {
443 int32_t ret;
444 struct DaiHost *daiHost = NULL;
445 AUDIO_DRIVER_LOG_DEBUG("entry.\n");
446
447 if (device == NULL) {
448 AUDIO_DRIVER_LOG_ERR("device is nullptr.");
449 return HDF_ERR_INVALID_OBJECT;
450 }
451
452 ret = DaiGetServiceName(device);
453 if (ret != HDF_SUCCESS) {
454 AUDIO_DRIVER_LOG_ERR("get service name fail.");
455 return ret;
456 }
457
458 ret = AudioSocRegisterDai(device, (void *)&g_dai_data);
459 if (ret != HDF_SUCCESS) {
460 AUDIO_DRIVER_LOG_ERR("register dai fail.");
461 return ret;
462 }
463
464 daiHost = (struct DaiHost *)device->service;
465
466 ret = DaiInit(daiHost, device);
467 if (ret != HDF_SUCCESS) {
468 AUDIO_DRIVER_LOG_ERR("Dai init fail.");
469 return ret;
470 }
471
472 AUDIO_DRIVER_LOG_DEBUG("success.\n");
473 return HDF_SUCCESS;
474 }
475
476 struct HdfDriverEntry g_dai_driver_entry = {
477 .moduleVersion = 1,
478 .moduleName = "DAI_IMX8",
479 .Bind = DaiDriverBind,
480 .Init = DaiDriverInit,
481 .Release = NULL,
482 };
483 HDF_INIT(g_dai_driver_entry);
484