mirror_ubuntu-kernels/drivers/soundwire/amd_init.c
Linus Torvalds 9ad55a67a7 soundwire updates for 6.13
- structure optimization of few bus structures and header updates
  - support for 2.0 disco spec
  - amd driver updates for acp revision, refactoring code and support for
    acp6.3
  - soft reset support for cadence driver
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmdEgiMACgkQfBQHDyUj
 g0dVBA/+MWwHs+Sl7LMSmkpGsfAsmSbD2il+v+9WcVnaQpl/dgv8EXPGbafBBgK/
 AlVUvLCNdbwY93wCb/2xdGPOJS699D7AtdJnUEppcL2VsMtEbgQxyG0OSekRVH0c
 NxVLNPVLQFQnZayh7MNflQNVrXJyEqUJg8n0G9G1KT7jTeMavejYhqmhN7TKNtLD
 vJzF79QFC2n7+f7jK9+d2pJlhW5V3XUyQCRF6FipftKbuZN+ciVh9kjnAf1GjPsi
 qpv7kRZ3ttZiYW+/8FjJxqChnT10b/ahRDwJTXE+uGhqxHD9Cjo/GYrzUtQQbDR2
 uvZ6+o0UxhN3HR5Dq09FJYPluHpt8S/s/wZ0dj+dXlvPR82qT6LA9LP16BFwYj3S
 36/DpGwJBYg3tsmwECKbY08t3aI1d8nXNKG0tXbkEU3RUWVeOJOLAyXbwYQ9DRGN
 k3RbTTEZiw223FlgAk9dzCI6mMuekdh20UWVH7iZwUl8ZvJhWNdWiZOV4uaUcGZS
 fmJ6JE7cM1ntv5rXjKIhhnTnoL5Z+3es3PjLxj8PE7VNC8Dlln67FF1NuoDd0uF0
 jWA13iNUOKgytsx2jxAxWnU8S3SAPjB1+GD65ovMxH+b9xtgwhtmCcpySJaG4/Pn
 P7F7dx1+bK8gbmc5xJf8ZddYeDF/Nb/493trk+Sf+zZSs+hevRY=
 =3Ob7
 -----END PGP SIGNATURE-----

Merge tag 'soundwire-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire

Pull soundwire updates from Vinod Koul:

 - structure optimization of few bus structures and header updates

 - support for 2.0 disco spec

 - amd driver updates for acp revision, refactoring code and support for
   acp6.3

 - soft reset support for cadence driver

* tag 'soundwire-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire: (24 commits)
  soundwire: Minor formatting fixups in sdw.h header
  soundwire: Update the includes on the sdw.h header
  soundwire: cadence: clear MCP BLOCK_WAKEUP in init
  soundwire: cadence: add soft-reset on startup
  soundwire: intel_auxdevice: add kernel parameter for mclk divider
  soundwire: mipi-disco: add support for DP0/DPn 'lane-list' property
  soundwire: mipi-disco: add new properties from 2.0 spec
  soundwire: mipi-disco: add comment on DP0-supported property
  soundwire: mipi-disco: add support for peripheral channelprepare timeout
  soundwire: mipi_disco: add support for clock-scales property
  soundwire: mipi-disco: add error handling for property array read
  soundwire: mipi-disco: remove DPn audio-modes
  soundwire: optimize sdw_dpn_prop
  soundwire: optimize sdw_dp0_prop
  soundwire: optimize sdw_slave_prop
  soundwire: optimize sdw_bus structure
  soundwire: optimize sdw_master_prop
  soundwire: optimize sdw_stream_runtime memory layout
  soundwire: mipi_disco: add MIPI-specific property_read_bool() helpers
  soundwire: Correct some typos in comments
  ...
2024-11-27 13:38:09 -08:00

232 lines
5.6 KiB
C

// 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 2024 Advanced Micro Devices, Inc.
*/
#include <linux/acpi.h>
#include <linux/cleanup.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_SDW0_PAD_CTRL_MASK 0x60
#define AMD_SDW1_PAD_CTRL_MASK 5
#define AMD_SDW_PAD_CTRL_MASK (AMD_SDW0_PAD_CTRL_MASK | AMD_SDW1_PAD_CTRL_MASK)
#define AMD_SDW0_PAD_EN 1
#define AMD_SDW1_PAD_EN 0x10
#define AMD_SDW_PAD_EN (AMD_SDW0_PAD_EN | AMD_SDW1_PAD_EN)
static int amd_enable_sdw_pads(void __iomem *mmio, u32 link_mask, struct device *dev)
{
u32 pad_keeper_en, pad_pulldown_ctrl_mask;
switch (link_mask) {
case 1:
pad_keeper_en = AMD_SDW0_PAD_EN;
pad_pulldown_ctrl_mask = AMD_SDW0_PAD_CTRL_MASK;
break;
case 2:
pad_keeper_en = AMD_SDW1_PAD_EN;
pad_pulldown_ctrl_mask = AMD_SDW1_PAD_CTRL_MASK;
break;
case 3:
pad_keeper_en = AMD_SDW_PAD_EN;
pad_pulldown_ctrl_mask = AMD_SDW_PAD_CTRL_MASK;
break;
default:
dev_err(dev, "No SDW Links are enabled\n");
return -ENODEV;
}
amd_updatel(mmio, ACP_SW_PAD_KEEPER_EN, pad_keeper_en, pad_keeper_en);
amd_updatel(mmio, ACP_PAD_PULLDOWN_CTRL, pad_pulldown_ctrl_mask, 0);
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 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;
struct resource *sdw_res __free(kfree) = 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;
sdw_pdata[index].acp_rev = res->acp_rev;
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]);
if (IS_ERR(ctx->pdev[index]))
goto err;
}
return ctx;
err:
while (index--) {
if (!(link_mask & BIT(index)))
continue;
platform_device_unregister(ctx->pdev[index]);
}
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->peripherals);
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->peripherals = kmalloc(struct_size(ctx->peripherals, array, num_slaves),
GFP_KERNEL);
if (!ctx->peripherals)
return -ENOMEM;
ctx->peripherals->num_peripherals = 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->peripherals->array[i] = slave;
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");