diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 0036f1c85ba72c..f143a651b6b757 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -4,6 +4,9 @@ config SND_SOC_SOF_PCI config SND_SOC_SOF_ACPI tristate +config SND_SOC_SOF_SPI + tristate + config SND_SOC_SOF tristate "Sound Open Firmware Support" select SND_SOC_TOPOLOGY diff --git a/sound/soc/sof/hw-spi.c b/sound/soc/sof/hw-spi.c index db5dcea73dee3e..47066f517e85c2 100644 --- a/sound/soc/sof/hw-spi.c +++ b/sound/soc/sof/hw-spi.c @@ -12,6 +12,8 @@ * Hardware interface for audio DSPs via SPI */ +#include +#include #include #include #include @@ -29,22 +31,69 @@ #include "sof-priv.h" #include "ops.h" -#include "intel.h" /* * Memory copy. */ -static void spi_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, - size_t size) +static void spi_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, + size_t size) { - // use spi_write() to copy data to DSP + u8 *buf; + int ret; + + if (offset) { + buf = kmalloc(size + offset, GFP_KERNEL); + if (!buf) { + dev_err(sdev->dev, "Buffer allocation failed\n"); + return; + } + } else { + buf = dest; + } + + ret = spi_read(to_spi_device(sdev->dev), buf, size + offset); + if (ret < 0) + dev_err(sdev->dev, "SPI read failed: %d\n", ret); + + if (offset) { + memcpy(dest, buf + offset, size); + kfree(buf); + } } -static void spi_block_read(struct snd_sof_dev *sdev, u32 offset, void *dest, - size_t size) +static void spi_block_write(struct snd_sof_dev *sdev, u32 offset, void *src, + size_t size) { - // use spi_read() to copy data from DSP + int ret; + u8 *buf; + + if (offset) { + buf = kmalloc(size + offset, GFP_KERNEL); + if (!buf) { + dev_err(sdev->dev, "Buffer allocation failed\n"); + return; + } + + /* Use Read-Modify-Wwrite */ + ret = spi_read(to_spi_device(sdev->dev), buf, size + offset); + if (ret < 0) { + dev_err(sdev->dev, "SPI read failed: %d\n", ret); + goto free; + } + + memcpy(buf + offset, src, size); + } else { + buf = src; + } + + ret = spi_write(to_spi_device(sdev->dev), buf, size + offset); + if (ret < 0) + dev_err(sdev->dev, "SPI write failed: %d\n", ret); + +free: + if (offset) + kfree(buf); } /* @@ -59,7 +108,7 @@ static int spi_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) // read local buffer with SPI data - dev_info(sdev->dev, " Firmware info: version %d:%d-%s build %d on %s:%s\n", + dev_info(sdev->dev, "Firmware info: version %d:%d-%s build %d on %s:%s\n", v->major, v->minor, v->tag, v->build, v->date, v->time); return 0; @@ -69,26 +118,25 @@ static int spi_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) * IPC Mailbox IO */ -static void spi_mailbox_write(struct snd_sof_dev *sdev, u32 offset, - void *message, size_t bytes) +static void spi_mailbox_write(struct snd_sof_dev *sdev __maybe_unused, + u32 offset __maybe_unused, + void *message __maybe_unused, + size_t bytes __maybe_unused) { - void __iomem *dest = sdev->bar[sdev->mailbox_bar] + offset; - - //memcpy_toio(dest, message, bytes); /* * this will copy to a local memory buffer that will be sent to DSP via * SPI at next IPC */ } -static void spi_mailbox_read(struct snd_sof_dev *sdev, u32 offset, +static void spi_mailbox_read(struct snd_sof_dev *sdev __maybe_unused, + u32 offset __maybe_unused, void *message, size_t bytes) { - void __iomem *src = sdev->bar[sdev->mailbox_bar] + offset; + memset(message, 0, bytes); - //memcpy_fromio(message, src, bytes); /* - * this will copy from a local memory buffer that will be received from + * this will copy from a local memory buffer that was received from * DSP via SPI at last IPC */ } @@ -97,9 +145,8 @@ static void spi_mailbox_read(struct snd_sof_dev *sdev, u32 offset, * IPC Doorbell IRQ handler and thread. */ -static irqreturn_t spi_irq_handler(int irq, void *context) +static irqreturn_t spi_irq_handler(int irq __maybe_unused, void *context __maybe_unused) { - struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; int ret = IRQ_NONE; // on SPI based devices this will likely come via a SoC GPIO IRQ @@ -109,25 +156,21 @@ static irqreturn_t spi_irq_handler(int irq, void *context) return ret; } -static irqreturn_t spi_irq_thread(int irq, void *context) +static irqreturn_t spi_irq_thread(int irq __maybe_unused, void *context __maybe_unused) { - struct snd_sof_dev *sdev = (struct snd_sof_dev *)context; - // read SPI data into local buffer and determine IPC cmd or reply /* * if reply. Handle Immediate reply from DSP Core and set DSP * state to ready */ - //snd_sof_ipc_reply(sdev, ipcx); /* if cmd, Handle messages from DSP Core */ - //snd_sof_ipc_msgs_rx(sdev); return IRQ_HANDLED; } -static int spi_is_ready(struct snd_sof_dev *sdev) +static int spi_is_ready(struct snd_sof_dev *sdev __maybe_unused) { // use local variable to store DSP command state. either DSP is ready // for new cmd or still processing current cmd. @@ -137,8 +180,6 @@ static int spi_is_ready(struct snd_sof_dev *sdev) static int spi_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { - u64 cmd = msg->header; - /* send the message */ spi_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, msg->msg_size); @@ -160,7 +201,7 @@ static int spi_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) } else { /* reply correct size ? */ if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected 0x%lx got 0x%x bytes\n", + dev_err(sdev->dev, "error: reply expected 0x%zu got 0x%x bytes\n", msg->reply_size, reply.hdr.size); size = msg->reply_size; ret = -EINVAL; @@ -183,22 +224,26 @@ static int spi_get_reply(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) static int spi_sof_probe(struct snd_sof_dev *sdev) { - struct snd_sof_pdata *pdata = sdev->pdata; - const struct sof_dev_desc *desc = pdata->desc; struct platform_device *pdev = container_of(sdev->parent, struct platform_device, dev); - int ret = 0; - /* get IRQ from Device tree or ACPI - register our IRQ */ + struct irq_data *irqd; + struct spi_device *spi = to_spi_device(pdev->dev.parent); + int ret; + + sdev->ipc_irq = spi->irq; dev_dbg(sdev->dev, "using IRQ %d\n", sdev->ipc_irq); - ret = request_threaded_irq(sdev->ipc_irq, spi_irq_handler, - spi_irq_thread, IRQF_SHARED, "AudioDSP", - sdev); - if (ret < 0) { + irqd = irq_get_irq_data(sdev->ipc_irq); + if (!irqd) + return -EINVAL; + + ret = devm_request_threaded_irq(sdev->dev, sdev->ipc_irq, + spi_irq_handler, spi_irq_thread, + irqd_get_trigger_type(irqd) | IRQF_ONESHOT, + "AudioDSP", sdev); + if (ret < 0) dev_err(sdev->dev, "error: failed to register IRQ %d\n", sdev->ipc_irq); - goto irq_err; - } return ret; } @@ -209,7 +254,12 @@ static int spi_sof_remove(struct snd_sof_dev *sdev) return 0; } -/* baytrail ops */ +static int spi_cmd_done(struct snd_sof_dev *sof_dev __maybe_unused, int dir __maybe_unused) +{ + return 0; +} + +/* SPI SOF ops */ struct snd_sof_dsp_ops snd_sof_spi_ops = { /* device init */ .probe = spi_sof_probe, @@ -235,9 +285,9 @@ struct snd_sof_dsp_ops snd_sof_spi_ops = { .cmd_done = spi_cmd_done, /* debug */ - .debug_map = spi_debugfs, - .debug_map_count = ARRAY_SIZE(spi_debugfs), - .dbg_dump = spi_dump, + .debug_map = NULL/*spi_debugfs*/, + .debug_map_count = 0/*ARRAY_SIZE(spi_debugfs)*/, + .dbg_dump = NULL/*spi_dump*/, /* module loading */ .load_module = snd_sof_parse_module_memcpy, diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index d777ddd80d893e..d41d60b037426e 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -9,6 +9,17 @@ config SND_SOC_SOF_INTEL Say Y if you have such a device. If unsure select "N". +config SND_SOC_SOF_SPIDSP + tristate "SOF support over the SPI bus" + depends on SND_SOC_SOF + depends on SPI + select SND_SOC_SOF_SPI + help + This adds support for Sound Open Firmware over SPI for Device + Tree based systems. + Say Y if you have such a device. + If unsure select "N". + if SND_SOC_SOF_INTEL config SND_SOC_SOF_BAYTRAIL diff --git a/sound/soc/sof/sof-spi-dev.c b/sound/soc/sof/sof-spi-dev.c index c55be66267f6a4..bc38e50941a2a9 100644 --- a/sound/soc/sof/sof-spi-dev.c +++ b/sound/soc/sof/sof-spi-dev.c @@ -17,7 +17,9 @@ #include #include #include +#include #include "sof-priv.h" +#include "hw-spi.h" static const struct dev_pm_ops sof_spi_pm = { SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) @@ -26,14 +28,47 @@ static const struct dev_pm_ops sof_spi_pm = { .suspend_late = snd_sof_suspend_late, }; +/* FIXME: replace with some meaningful values */ +static struct snd_sof_machine sof_spi_machines[] = { + { + .id = "INT343A", + .drv_name = "bxt_alc298s_i2s", + .sof_fw_filename = "intel/sof-spi.ri", + .sof_tplg_filename = "intel/sof-spi.tplg", + .asoc_plat_name = "0000:00:0e.0", + .ops = &snd_sof_spi_ops, + }, +}; + +static const struct sof_dev_desc spi_desc = { + .sof_machines = sof_spi_machines, + .nocodec_fw_filename = "intel/sof-spi.ri", + .nocodec_tplg_filename = "intel/sof-spi.tplg", + .resindex_lpe_base = -1, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, +}; + static int sof_spi_probe(struct spi_device *spi) { struct device *dev = &spi->dev; - const struct snd_sof_machine *mach; - struct snd_sof_machine *m; + const struct sof_dev_desc *desc = of_device_get_match_data(dev); + struct snd_sof_machine *machines; + struct snd_sof_machine *mach; struct snd_sof_pdata *sof_pdata; struct sof_platform_priv *priv; - int ret = 0; + const char *tplg, *fw; + struct gpio_desc *gpiod; + int ret, irq; + + if (!dev->of_node || !desc) + return -ENODEV; + + machines = desc->sof_machines; + if (!machines) + return -ENODEV; dev_dbg(&spi->dev, "SPI DSP detected"); @@ -46,24 +81,49 @@ static int sof_spi_probe(struct spi_device *spi) if (!sof_pdata) return -ENOMEM; + ret = of_property_read_string(dev->of_node, "tplg_filename", &tplg); + if (ret < 0 || !tplg) + return -EINVAL; + + ret = of_property_read_string(dev->of_node, "fw_filename", &fw); + if (ret < 0 || !fw) + return -EINVAL; + + /* + * Get an IRQ GPIO descriptor from an "irq-gpios" property + * If the IRQ is optional, use devm_gpiod_get_optional() + */ + gpiod = devm_gpiod_get(dev, "irq", GPIOD_IN); + if (IS_ERR(gpiod)) + return PTR_ERR(gpiod); + + irq = gpiod_to_irq(gpiod); + if (irq < 0) + return irq; + + /* TODO: add any required regulators */ + /* use nocodec machine atm */ dev_err(dev, "No matching ASoC machine driver found - using nocodec\n"); sof_pdata->drv_name = "sof-nocodec"; - m = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); - if (!m) + mach = devm_kzalloc(dev, sizeof(*mach), GFP_KERNEL); + if (!mach) return -ENOMEM; - m->drv_name = "sof-nocodec"; - m->sof_fw_filename = desc->nocodec_fw_filename; - m->sof_tplg_filename = desc->nocodec_tplg_filename; - m->ops = desc->machines[0].ops; - m->asoc_plat_name = "sof-platform"; - mach = m; - - sof_pdata->id = pci_id->device; - sof_pdata->name = spi_name(spi); - sof_pdata->machine = mach; - sof_pdata->desc = (struct sof_dev_desc *)pci_id->driver_data; + mach->drv_name = "sof-nocodec"; + /* + * desc->nocodec_*_filename are selected as long as nocodec is used. + * Later machine->*_filename will have to be used. + */ + mach->sof_fw_filename = desc->nocodec_fw_filename; + mach->sof_tplg_filename = desc->nocodec_tplg_filename; + mach->ops = machines[0].ops; + mach->asoc_plat_name = "sof-platform"; + + sof_pdata->id = -1; + sof_pdata->name = dev_name(&spi->dev); + sof_pdata->sof_machine = mach; + sof_pdata->desc = desc; priv->sof_pdata = sof_pdata; sof_pdata->dev = dev; sof_pdata->type = SOF_DEVICE_SPI; @@ -74,6 +134,7 @@ static int sof_spi_probe(struct spi_device *spi) sof_pdata, sizeof(*sof_pdata)); if (IS_ERR(sof_pdata->pdev_mach)) return PTR_ERR(sof_pdata->pdev_mach); + dev_dbg(dev, "created machine %s\n", dev_name(&sof_pdata->pdev_mach->dev)); @@ -85,6 +146,8 @@ static int sof_spi_probe(struct spi_device *spi) return ret; } + spi->irq = irq; + /* allow runtime_pm */ pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY); pm_runtime_use_autosuspend(dev); @@ -102,28 +165,24 @@ static int sof_spi_remove(struct spi_device *spi) if (!IS_ERR_OR_NULL(priv->pdev_pcm)) platform_device_unregister(priv->pdev_pcm); release_firmware(sof_pdata->fw); + + return 0; } -static struct spi_driver wm8731_spi_driver = { +const struct of_device_id sof_of_match[] = { + { .compatible = "sof,spi-sue-creek", .data = &spi_desc }, + { } +}; + +static struct spi_driver sof_spi_driver = { .driver = { - .name = "sof-spi-dev", +o .name = "sof-spi-dev", .of_match_table = sof_of_match, }, .probe = sof_spi_probe, .remove = sof_spi_remove, }; -static const struct snd_sof_machine sof_spi_machines[] = { - { "INT343A", "bxt_alc298s_i2s", "intel/sof-spi.ri", - "intel/sof-spi.tplg", "0000:00:0e.0", &snd_sof_spi_ops }, -}; - -static const struct sof_dev_desc spi_desc = { - .machines = sof_spi_machines, - .nocodec_fw_filename = "intel/sof-spi.ri", - .nocodec_tplg_filename = "intel/sof-spi.tplg" -}; - static int __init sof_spi_modinit(void) { int ret;