Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

soundwire/SOF: add SoundWire Interface support for AMD SOF stack #4699

Merged
merged 12 commits into from Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion drivers/soundwire/Makefile
Expand Up @@ -20,7 +20,7 @@ soundwire-bus-y += irq.o
endif

#AMD driver
soundwire-amd-y := amd_manager.o
soundwire-amd-y := amd_init.o amd_manager.o
vijendarmukunda marked this conversation as resolved.
Show resolved Hide resolved
obj-$(CONFIG_SOUNDWIRE_AMD) += soundwire-amd.o

#Cadence Objs
Expand Down
235 changes: 235 additions & 0 deletions drivers/soundwire/amd_init.c
@@ -0,0 +1,235 @@
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
* SoundWire AMD Manager Initialize routines
*
* Initializes and creates SDW devices based on ACPI and Hardware values
*
* Copyright 2023 Advanced Micro Devices, Inc.
*/

#include <linux/acpi.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>

#include "amd_init.h"

#define ACP_PAD_PULLDOWN_CTRL 0x0001448
#define ACP_SW_PAD_KEEPER_EN 0x0001454
#define AMD_SDW_PAD_PULLDOWN_CTRL_ENABLE_MASK 0x7f9a
#define AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK 0x7f9f
#define AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK 0x7ffa
#define AMD_SDW0_PAD_EN_MASK 1
#define AMD_SDW1_PAD_EN_MASK 0x10
#define AMD_SDW_PAD_EN_MASK (AMD_SDW0_PAD_EN_MASK | AMD_SDW1_PAD_EN_MASK)

static int amd_enable_sdw_pads(void __iomem *mmio, u32 link_mask, struct device *dev)
{
u32 val;
u32 pad_keeper_en_mask, pad_pulldown_ctrl_mask;

switch (link_mask) {
case 1:
pad_keeper_en_mask = AMD_SDW0_PAD_EN_MASK;
pad_pulldown_ctrl_mask = AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK;
break;
case 2:
pad_keeper_en_mask = AMD_SDW1_PAD_EN_MASK;
pad_pulldown_ctrl_mask = AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK;
break;
case 3:
pad_keeper_en_mask = AMD_SDW_PAD_EN_MASK;
pad_pulldown_ctrl_mask = AMD_SDW_PAD_PULLDOWN_CTRL_ENABLE_MASK;
break;
default:
dev_err(dev, "No SDW Links are enabled\n");
return -ENODEV;
}

val = readl(mmio + ACP_SW_PAD_KEEPER_EN);
val |= pad_keeper_en_mask;
writel(val, mmio + ACP_SW_PAD_KEEPER_EN);
val = readl(mmio + ACP_PAD_PULLDOWN_CTRL);
val &= pad_pulldown_ctrl_mask;
vijendarmukunda marked this conversation as resolved.
Show resolved Hide resolved
writel(val, mmio + ACP_PAD_PULLDOWN_CTRL);
return 0;
}

static int sdw_amd_cleanup(struct sdw_amd_ctx *ctx)
{
int i;

for (i = 0; i < ctx->count; i++) {
if (!(ctx->link_mask & BIT(i)))
continue;
platform_device_unregister(ctx->pdev[i]);
}

return 0;
}

static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res)
{
struct sdw_amd_ctx *ctx;
struct acpi_device *adev;
struct resource *sdw_res;
struct acp_sdw_pdata sdw_pdata[2];
struct platform_device_info pdevinfo[2];
u32 link_mask;
int count, index;
int ret;

if (!res)
return NULL;

adev = acpi_fetch_acpi_dev(res->handle);
if (!adev)
return NULL;

if (!res->count)
return NULL;

count = res->count;
dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count);
ret = amd_enable_sdw_pads(res->mmio_base, res->link_mask, res->parent);
if (ret)
return NULL;

/*
* we need to alloc/free memory manually and can't use devm:
* this routine may be called from a workqueue, and not from
* the parent .probe.
* If devm_ was used, the memory might never be freed on errors.
*/
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return NULL;

ctx->count = count;
ctx->link_mask = res->link_mask;
sdw_res = kzalloc(sizeof(*sdw_res), GFP_KERNEL);
if (!sdw_res) {
kfree(ctx);
return NULL;
}
sdw_res->flags = IORESOURCE_MEM;
sdw_res->start = res->addr;
sdw_res->end = res->addr + res->reg_range;
memset(&pdevinfo, 0, sizeof(pdevinfo));
link_mask = ctx->link_mask;
for (index = 0; index < count; index++) {
if (!(link_mask & BIT(index)))
continue;

sdw_pdata[index].instance = index;
sdw_pdata[index].acp_sdw_lock = res->acp_lock;
pdevinfo[index].name = "amd_sdw_manager";
pdevinfo[index].id = index;
pdevinfo[index].parent = res->parent;
pdevinfo[index].num_res = 1;
pdevinfo[index].res = sdw_res;
pdevinfo[index].data = &sdw_pdata[index];
pdevinfo[index].size_data = sizeof(struct acp_sdw_pdata);
pdevinfo[index].fwnode = acpi_fwnode_handle(adev);
ctx->pdev[index] = platform_device_register_full(&pdevinfo[index]);
vijendarmukunda marked this conversation as resolved.
Show resolved Hide resolved
if (IS_ERR(ctx->pdev[index]))
goto err;
}
kfree(sdw_res);
return ctx;
err:
while (index--) {
if (!(link_mask & BIT(index)))
continue;

platform_device_unregister(ctx->pdev[index]);
}

kfree(sdw_res);
kfree(ctx);
return NULL;
}

static int sdw_amd_startup(struct sdw_amd_ctx *ctx)
{
struct amd_sdw_manager *amd_manager;
int i, ret;

/* Startup SDW Manager devices */
for (i = 0; i < ctx->count; i++) {
if (!(ctx->link_mask & BIT(i)))
continue;
amd_manager = dev_get_drvdata(&ctx->pdev[i]->dev);
ret = amd_sdw_manager_start(amd_manager);
if (ret)
return ret;
}

return 0;
}

int sdw_amd_probe(struct sdw_amd_res *res, struct sdw_amd_ctx **sdw_ctx)
{
*sdw_ctx = sdw_amd_probe_controller(res);
if (!*sdw_ctx)
return -ENODEV;

return sdw_amd_startup(*sdw_ctx);
}
EXPORT_SYMBOL_NS(sdw_amd_probe, SOUNDWIRE_AMD_INIT);

void sdw_amd_exit(struct sdw_amd_ctx *ctx)
{
sdw_amd_cleanup(ctx);
kfree(ctx->ids);
kfree(ctx);
}
EXPORT_SYMBOL_NS(sdw_amd_exit, SOUNDWIRE_AMD_INIT);

int sdw_amd_get_slave_info(struct sdw_amd_ctx *ctx)
{
struct amd_sdw_manager *amd_manager;
struct sdw_bus *bus;
struct sdw_slave *slave;
struct list_head *node;
int index;
int i = 0;
int num_slaves = 0;

for (index = 0; index < ctx->count; index++) {
if (!(ctx->link_mask & BIT(index)))
continue;
amd_manager = dev_get_drvdata(&ctx->pdev[index]->dev);
if (!amd_manager)
return -ENODEV;
bus = &amd_manager->bus;
/* Calculate number of slaves */
list_for_each(node, &bus->slaves)
num_slaves++;
}

ctx->ids = kcalloc(num_slaves, sizeof(*ctx->ids), GFP_KERNEL);
if (!ctx->ids)
return -ENOMEM;
ctx->num_slaves = num_slaves;
for (index = 0; index < ctx->count; index++) {
if (!(ctx->link_mask & BIT(index)))
continue;
amd_manager = dev_get_drvdata(&ctx->pdev[index]->dev);
if (amd_manager) {
bus = &amd_manager->bus;
list_for_each_entry(slave, &bus->slaves, node) {
ctx->ids[i].id = slave->id;
ctx->ids[i].link_id = bus->link_id;
i++;
}
}
}
return 0;
}
EXPORT_SYMBOL_NS(sdw_amd_get_slave_info, SOUNDWIRE_AMD_INIT);

MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
MODULE_DESCRIPTION("AMD SoundWire Init Library");
MODULE_LICENSE("Dual BSD/GPL");
13 changes: 13 additions & 0 deletions drivers/soundwire/amd_init.h
@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
/*
* Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
*/

#ifndef __AMD_INIT_H
#define __AMD_INIT_H

#include <linux/soundwire/sdw_amd.h>

int amd_sdw_manager_start(struct amd_sdw_manager *amd_manager);

#endif
41 changes: 7 additions & 34 deletions drivers/soundwire/amd_manager.c
Expand Up @@ -19,29 +19,13 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "bus.h"
#include "amd_init.h"
#include "amd_manager.h"

#define DRV_NAME "amd_sdw_manager"

#define to_amd_sdw(b) container_of(b, struct amd_sdw_manager, bus)

static void amd_enable_sdw_pads(struct amd_sdw_manager *amd_manager)
{
u32 sw_pad_pulldown_val;
u32 val;

mutex_lock(amd_manager->acp_sdw_lock);
val = readl(amd_manager->acp_mmio + ACP_SW_PAD_KEEPER_EN);
val |= amd_manager->reg_mask->sw_pad_enable_mask;
writel(val, amd_manager->acp_mmio + ACP_SW_PAD_KEEPER_EN);
usleep_range(1000, 1500);

sw_pad_pulldown_val = readl(amd_manager->acp_mmio + ACP_PAD_PULLDOWN_CTRL);
sw_pad_pulldown_val &= amd_manager->reg_mask->sw_pad_pulldown_mask;
writel(sw_pad_pulldown_val, amd_manager->acp_mmio + ACP_PAD_PULLDOWN_CTRL);
mutex_unlock(amd_manager->acp_sdw_lock);
}

static int amd_init_sdw_manager(struct amd_sdw_manager *amd_manager)
{
u32 val;
Expand Down Expand Up @@ -102,12 +86,11 @@ static int amd_disable_sdw_manager(struct amd_sdw_manager *amd_manager)

static void amd_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
{
struct sdw_manager_reg_mask *reg_mask = amd_manager->reg_mask;
u32 val;

mutex_lock(amd_manager->acp_sdw_lock);
val = readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance));
val |= reg_mask->acp_sdw_intr_mask;
val |= sdw_manager_reg_mask_array[amd_manager->instance];
writel(val, amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance));
mutex_unlock(amd_manager->acp_sdw_lock);

Expand All @@ -120,12 +103,11 @@ static void amd_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager)

static void amd_disable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
{
struct sdw_manager_reg_mask *reg_mask = amd_manager->reg_mask;
u32 val;

mutex_lock(amd_manager->acp_sdw_lock);
val = readl(amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance));
val &= ~reg_mask->acp_sdw_intr_mask;
val &= ~sdw_manager_reg_mask_array[amd_manager->instance];
writel(val, amd_manager->acp_mmio + ACP_EXTERNAL_INTR_CNTL(amd_manager->instance));
mutex_unlock(amd_manager->acp_sdw_lock);

Expand Down Expand Up @@ -864,23 +846,20 @@ static void amd_sdw_irq_thread(struct work_struct *work)
writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_0TO7);
}

static void amd_sdw_probe_work(struct work_struct *work)
int amd_sdw_manager_start(struct amd_sdw_manager *amd_manager)
{
struct amd_sdw_manager *amd_manager = container_of(work, struct amd_sdw_manager,
probe_work);
struct sdw_master_prop *prop;
int ret;

prop = &amd_manager->bus.prop;
if (!prop->hw_disabled) {
amd_enable_sdw_pads(amd_manager);
ret = amd_init_sdw_manager(amd_manager);
if (ret)
return;
return ret;
amd_enable_sdw_interrupts(amd_manager);
ret = amd_enable_sdw_manager(amd_manager);
if (ret)
return;
return ret;
amd_sdw_set_frameshape(amd_manager);
}
/* Enable runtime PM */
Expand All @@ -889,6 +868,7 @@ static void amd_sdw_probe_work(struct work_struct *work)
pm_runtime_mark_last_busy(amd_manager->dev);
pm_runtime_set_active(amd_manager->dev);
pm_runtime_enable(amd_manager->dev);
return 0;
}

static int amd_sdw_manager_probe(struct platform_device *pdev)
Expand Down Expand Up @@ -948,7 +928,6 @@ static int amd_sdw_manager_probe(struct platform_device *pdev)
return -EINVAL;
}

amd_manager->reg_mask = &sdw_manager_reg_mask_array[amd_manager->instance];
params = &amd_manager->bus.params;
params->max_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
params->curr_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
Expand All @@ -972,11 +951,6 @@ static int amd_sdw_manager_probe(struct platform_device *pdev)
dev_set_drvdata(dev, amd_manager);
INIT_WORK(&amd_manager->amd_sdw_irq_thread, amd_sdw_irq_thread);
INIT_WORK(&amd_manager->amd_sdw_work, amd_sdw_update_slave_status_work);
INIT_WORK(&amd_manager->probe_work, amd_sdw_probe_work);
/*
* Instead of having lengthy probe sequence, use deferred probe.
*/
schedule_work(&amd_manager->probe_work);
return 0;
}

Expand All @@ -986,7 +960,6 @@ static void amd_sdw_manager_remove(struct platform_device *pdev)
int ret;

pm_runtime_disable(&pdev->dev);
cancel_work_sync(&amd_manager->probe_work);
amd_disable_sdw_interrupts(amd_manager);
sdw_bus_master_delete(&amd_manager->bus);
ret = amd_disable_sdw_manager(amd_manager);
Expand Down
12 changes: 2 additions & 10 deletions drivers/soundwire/amd_manager.h
Expand Up @@ -243,16 +243,8 @@ static struct sdw_manager_dp_reg sdw1_manager_dp_reg[AMD_SDW1_MAX_DAI] = {
ACP_SW_AUDIO1_RX_OFFSET, ACP_SW_AUDIO1_RX_CHANNEL_ENABLE_DP0}
};

static struct sdw_manager_reg_mask sdw_manager_reg_mask_array[2] = {
{
AMD_SDW0_PAD_KEEPER_EN_MASK,
AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK,
AMD_SDW0_EXT_INTR_MASK
},
{
AMD_SDW1_PAD_KEEPER_EN_MASK,
AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK,
static u32 sdw_manager_reg_mask_array[AMD_SDW_MAX_MANAGER_COUNT] = {
AMD_SDW0_EXT_INTR_MASK,
AMD_SDW1_EXT_INTR_MASK
}
};
#endif