/* sound/soc/samsung/vts/vts-plat.c * * ALSA SoC - Samsung VTS platfrom driver * * Copyright (c) 2016 Samsung Electronics Co. Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ //#define DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vts.h" static const struct snd_pcm_hardware vts_platform_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID, .formats = SNDRV_PCM_FMTBIT_S16, .rates = SNDRV_PCM_RATE_16000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = BUFFER_BYTES_MAX, .period_bytes_min = PERIOD_BYTES_MIN, .period_bytes_max = PERIOD_BYTES_MAX, .periods_min = BUFFER_BYTES_MAX / PERIOD_BYTES_MAX, .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN, }; static int vts_platform_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; struct device *dev = platform->dev; struct vts_platform_data *data = dev_get_drvdata(dev); struct snd_pcm_runtime *runtime = substream->runtime; if (data->type == PLATFORM_VTS_TRIGGER_RECORD) { snd_pcm_set_runtime_buffer(substream, &data->vts_data->dmab); } else { snd_pcm_set_runtime_buffer(substream, &data->vts_data->dmab_rec); } dev_info(dev, "%s:%s:DmaAddr=%pad Total=%zu PrdSz=%u(%u) #Prds=%u dma_area=%p\n", __func__, snd_pcm_stream_str(substream), &runtime->dma_addr, runtime->dma_bytes, params_period_size(params), params_period_bytes(params), params_periods(params), runtime->dma_area); data->pointer = 0; return 0; } static int vts_platform_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; struct device *dev = platform->dev; dev_dbg(dev, "%s\n", __func__); return 0; } static int vts_platform_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; struct device *dev = platform->dev; dev_info(dev, "%s\n", __func__); return 0; } static int vts_platform_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; struct device *dev = platform->dev; struct vts_platform_data *data = dev_get_drvdata(dev); u32 values[3] = {0,0,0}; int result = 0; dev_info(dev, "%s ++ CMD: %d\n", __func__, cmd); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (data->type == PLATFORM_VTS_TRIGGER_RECORD) { dev_dbg(dev, "%s VTS_IRQ_AP_START_COPY\n", __func__); result = vts_start_ipc_transaction(dev, data->vts_data, VTS_IRQ_AP_START_COPY, &values, 1, 1); } else { dev_dbg(dev, "%s VTS_IRQ_AP_START_REC\n", __func__); result = vts_start_ipc_transaction(dev, data->vts_data, VTS_IRQ_AP_START_REC, &values, 1, 1); } break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (data->type == PLATFORM_VTS_TRIGGER_RECORD) { dev_dbg(dev, "%s VTS_IRQ_AP_STOP_COPY\n", __func__); result = vts_start_ipc_transaction(dev, data->vts_data, VTS_IRQ_AP_STOP_COPY, &values, 1, 1); } else { dev_dbg(dev, "%s VTS_IRQ_AP_STOP_REC\n", __func__); result = vts_start_ipc_transaction(dev, data->vts_data, VTS_IRQ_AP_STOP_REC, &values, 1, 1); } break; default: result = -EINVAL; break; } dev_info(dev, "%s -- CMD: %d\n", __func__, cmd); return result; } static snd_pcm_uframes_t vts_platform_pointer(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; struct device *dev = platform->dev; struct vts_platform_data *data = dev_get_drvdata(dev); struct snd_pcm_runtime *runtime = substream->runtime; dev_dbg(dev, "%s: pointer=%08x\n", __func__, data->pointer); return bytes_to_frames(runtime, data->pointer); } static int vts_platform_open(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; struct device *dev = platform->dev; struct vts_platform_data *data = dev_get_drvdata(dev); int result = 0; dev_info(dev, "%s\n", __func__); if (data->vts_data->voicecall_enabled) { dev_warn(dev, "%s VTS SRAM is Used for CP call\n", __func__); return -EBUSY; } pm_runtime_get_sync(dev); snd_soc_set_runtime_hwparams(substream, &vts_platform_hardware); if (data->type == PLATFORM_VTS_NORMAL_RECORD) { dev_info(dev, "%s open --\n", __func__); result = vts_set_dmicctrl(data->vts_data->pdev, VTS_MICCONF_FOR_RECORD, true); if (result < 0) { dev_err(dev, "%s: MIC control configuration failed\n", __func__); pm_runtime_put_sync(dev); } } return result; } static int vts_platform_close(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; struct device *dev = platform->dev; struct vts_platform_data *data = dev_get_drvdata(dev); int result = 0; dev_info(dev, "%s\n", __func__); if (data->vts_data->voicecall_enabled) { dev_warn(dev, "%s VTS SRAM is Used for CP call\n", __func__); return -EBUSY; } if (data->type == PLATFORM_VTS_NORMAL_RECORD) { dev_info(dev, "%s close --\n", __func__); result = vts_set_dmicctrl(data->vts_data->pdev, VTS_MICCONF_FOR_RECORD, false); if (result < 0) dev_warn(dev, "%s: MIC control configuration failed\n", __func__); } pm_runtime_put_sync(dev); return result; } static int vts_platform_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; struct device *dev = platform->dev; struct snd_pcm_runtime *runtime = substream->runtime; dev_info(dev, "%s\n", __func__); return dma_mmap_writecombine(dev, vma, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); } static struct snd_pcm_ops vts_platform_ops = { .open = vts_platform_open, .close = vts_platform_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = vts_platform_hw_params, .hw_free = vts_platform_hw_free, .prepare = vts_platform_prepare, .trigger = vts_platform_trigger, .pointer = vts_platform_pointer, .mmap = vts_platform_mmap, }; static int vts_platform_new(struct snd_soc_pcm_runtime *runtime) { struct snd_soc_platform *platform = runtime->platform; struct device *dev = platform->dev; struct vts_platform_data *data = dev_get_drvdata(dev); struct snd_pcm_substream *substream = runtime->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; dev_info(dev, "%s \n", __func__); data->substream = substream; dev_info(dev, "%s Update Soc Card from runtime!!\n", __func__); data->vts_data->card = runtime->card; return 0; } static void vts_platform_free(struct snd_pcm *pcm) { return; } static const struct snd_soc_platform_driver vts_dma = { .ops = &vts_platform_ops, .pcm_new = vts_platform_new, .pcm_free = vts_platform_free, }; static int samsung_vts_dma_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct device_node *np_vts; struct vts_platform_data *data; int result; const char *type; dev_info(dev, "%s \n", __func__); data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) { dev_err(dev, "Failed to allocate memory\n"); return -ENOMEM; } platform_set_drvdata(pdev, data); np_vts = of_parse_phandle(np, "vts", 0); if (!np_vts) { dev_err(dev, "Failed to get vts device node\n"); return -EPROBE_DEFER; } data->pdev_vts = of_find_device_by_node(np_vts); if (!data->pdev_vts) { dev_err(dev, "Failed to get vts platform device\n"); return -EPROBE_DEFER; } data->vts_data = platform_get_drvdata(data->pdev_vts); result = of_property_read_u32_index(np, "id", 0, &data->id); if (result < 0) { dev_err(dev, "id property reading fail\n"); return result; } result = of_property_read_string(np, "type", &type); if (result < 0) { dev_err(dev, "type property reading fail\n"); return result; } if (!strncmp(type, "vts-record", sizeof("vts-record"))) { data->type = PLATFORM_VTS_NORMAL_RECORD; dev_info(dev, "%s - vts-record Probed \n", __func__); } else { data->type = PLATFORM_VTS_TRIGGER_RECORD; dev_info(dev, "%s - vts-trigger-record Probed \n", __func__); } vts_register_dma(data->vts_data->pdev, pdev, data->id); return snd_soc_register_platform(&pdev->dev, &vts_dma); } static int samsung_vts_dma_remove(struct platform_device *pdev) { snd_soc_unregister_platform(&pdev->dev); return 0; } static const struct of_device_id samsung_vts_dma_match[] = { { .compatible = "samsung,vts-dma", }, {}, }; MODULE_DEVICE_TABLE(of, samsung_vts_dma_match); static struct platform_driver samsung_vts_dma_driver = { .probe = samsung_vts_dma_probe, .remove = samsung_vts_dma_remove, .driver = { .name = "samsung-vts-dma", .owner = THIS_MODULE, .of_match_table = of_match_ptr(samsung_vts_dma_match), }, }; module_platform_driver(samsung_vts_dma_driver); /* Module information */ MODULE_AUTHOR("Palli Satish Kumar Reddy, "); MODULE_DESCRIPTION("Samsung VTS DMA"); MODULE_ALIAS("platform:samsung-vts-dma"); MODULE_LICENSE("GPL");