File Index
Files and directories
Sign in
FILE

sdca_class.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/sdca/sdca_class.c C
  1// SPDX-License-Identifier: GPL-2.0
  2// Copyright (C) 2025 Cirrus Logic, Inc. and
  3//                    Cirrus Logic International Semiconductor Ltd.
  4
  5/*
  6 * The MIPI SDCA specification is available for public downloads at
  7 * https://www.mipi.org/mipi-sdca-v1-0-download
  8 */
  9
 10#include <linux/device.h>
 11#include <linux/err.h>
 12#include <linux/mod_devicetable.h>
 13#include <linux/module.h>
 14#include <linux/pm.h>
 15#include <linux/pm_runtime.h>
 16#include <linux/regmap.h>
 17#include <linux/soundwire/sdw.h>
 18#include <linux/soundwire/sdw_registers.h>
 19#include <linux/soundwire/sdw_type.h>
 20#include <sound/sdca.h>
 21#include <sound/sdca_function.h>
 22#include <sound/sdca_interrupts.h>
 23#include <sound/sdca_regmap.h>
 24#include "sdca_class.h"
 25
 26#define CLASS_SDW_ATTACH_TIMEOUT_MS	5000
 27
 28static int class_read_prop(struct sdw_slave *sdw)
 29{
 30	struct sdw_slave_prop *prop = &sdw->prop;
 31
 32	sdw_slave_read_prop(sdw);
 33
 34	prop->use_domain_irq = true;
 35	prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY |
 36			      SDW_SCP_INT1_IMPL_DEF;
 37
 38	return 0;
 39}
 40
 41static int class_sdw_update_status(struct sdw_slave *sdw, enum sdw_slave_status status)
 42{
 43	struct sdca_class_drv *drv = dev_get_drvdata(&sdw->dev);
 44
 45	switch (status) {
 46	case SDW_SLAVE_ATTACHED:
 47		dev_dbg(drv->dev, "device attach\n");
 48
 49		drv->attached = true;
 50
 51		complete(&drv->device_attach);
 52		break;
 53	case SDW_SLAVE_UNATTACHED:
 54		dev_dbg(drv->dev, "device detach\n");
 55
 56		drv->attached = false;
 57
 58		reinit_completion(&drv->device_attach);
 59		break;
 60	default:
 61		break;
 62	}
 63
 64	return 0;
 65}
 66
 67static const struct sdw_slave_ops class_sdw_ops = {
 68	.read_prop	= class_read_prop,
 69	.update_status	= class_sdw_update_status,
 70};
 71
 72static void class_regmap_lock(void *data)
 73{
 74	struct mutex *lock = data;
 75
 76	mutex_lock(lock);
 77}
 78
 79static void class_regmap_unlock(void *data)
 80{
 81	struct mutex *lock = data;
 82
 83	mutex_unlock(lock);
 84}
 85
 86static int class_wait_for_attach(struct sdca_class_drv *drv)
 87{
 88	if (!drv->attached) {
 89		unsigned long timeout = msecs_to_jiffies(CLASS_SDW_ATTACH_TIMEOUT_MS);
 90		unsigned long time;
 91
 92		time = wait_for_completion_timeout(&drv->device_attach, timeout);
 93		if (!time) {
 94			dev_err(drv->dev, "timed out waiting for device re-attach\n");
 95			return -ETIMEDOUT;
 96		}
 97	}
 98
 99	regcache_cache_only(drv->dev_regmap, false);
100
101	return 0;
102}
103
104static bool class_dev_regmap_volatile(struct device *dev, unsigned int reg)
105{
106	switch (reg) {
107	case SDW_SCP_SDCA_INTMASK1 ... SDW_SCP_SDCA_INTMASK4:
108		return false;
109	default:
110		return true;
111	}
112}
113
114static bool class_dev_regmap_precious(struct device *dev, unsigned int reg)
115{
116	switch (reg) {
117	case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INT4:
118	case SDW_SCP_SDCA_INTMASK1 ... SDW_SCP_SDCA_INTMASK4:
119		return false;
120	default:
121		return true;
122	}
123}
124
125static const struct regmap_config class_dev_regmap_config = {
126	.name			= "sdca-device",
127	.reg_bits		= 32,
128	.val_bits		= 8,
129
130	.max_register		= SDW_SDCA_MAX_REGISTER,
131	.volatile_reg		= class_dev_regmap_volatile,
132	.precious_reg		= class_dev_regmap_precious,
133
134	.cache_type		= REGCACHE_MAPLE,
135
136	.lock			= class_regmap_lock,
137	.unlock			= class_regmap_unlock,
138};
139
140static void class_boot_work(struct work_struct *work)
141{
142	struct sdca_class_drv *drv = container_of(work,
143						  struct sdca_class_drv,
144						  boot_work);
145	int ret;
146
147	ret = class_wait_for_attach(drv);
148	if (ret)
149		goto err;
150
151	drv->irq_info = sdca_irq_allocate(drv->dev, drv->dev_regmap,
152					  drv->sdw->irq);
153	if (IS_ERR(drv->irq_info))
154		goto err;
155
156	ret = sdca_dev_register_functions(drv->sdw);
157	if (ret)
158		goto err;
159
160	dev_dbg(drv->dev, "boot work complete\n");
161
162	pm_runtime_mark_last_busy(drv->dev);
163	pm_runtime_put_autosuspend(drv->dev);
164
165	return;
166
167err:
168	pm_runtime_put_sync(drv->dev);
169}
170
171static void class_dev_remove(void *data)
172{
173	struct sdca_class_drv *drv = data;
174
175	cancel_work_sync(&drv->boot_work);
176
177	sdca_dev_unregister_functions(drv->sdw);
178}
179
180static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id)
181{
182	struct device *dev = &sdw->dev;
183	struct sdca_device_data *data = &sdw->sdca_data;
184	struct regmap_config *dev_config;
185	struct sdca_class_drv *drv;
186	int ret;
187
188	sdca_lookup_swft(sdw);
189
190	drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
191	if (!drv)
192		return -ENOMEM;
193
194	dev_config = devm_kmemdup(dev, &class_dev_regmap_config,
195				  sizeof(*dev_config), GFP_KERNEL);
196	if (!dev_config)
197		return -ENOMEM;
198
199	drv->functions = devm_kcalloc(dev, data->num_functions,
200				      sizeof(*drv->functions),
201				      GFP_KERNEL);
202	if (!drv->functions)
203		return -ENOMEM;
204
205	drv->dev = dev;
206	drv->sdw = sdw;
207	mutex_init(&drv->regmap_lock);
208
209	dev_set_drvdata(drv->dev, drv);
210
211	INIT_WORK(&drv->boot_work, class_boot_work);
212	init_completion(&drv->device_attach);
213
214	dev_config->lock_arg = &drv->regmap_lock;
215
216	drv->dev_regmap = devm_regmap_init_sdw(sdw, dev_config);
217	if (IS_ERR(drv->dev_regmap))
218		return dev_err_probe(drv->dev, PTR_ERR(drv->dev_regmap),
219				     "failed to create device regmap\n");
220
221	regcache_cache_only(drv->dev_regmap, true);
222
223	pm_runtime_set_autosuspend_delay(dev, 250);
224	pm_runtime_use_autosuspend(dev);
225	pm_runtime_set_active(dev);
226	pm_runtime_get_noresume(dev);
227
228	ret = devm_pm_runtime_enable(dev);
229	if (ret)
230		return ret;
231
232	ret = devm_add_action_or_reset(dev, class_dev_remove, drv);
233	if (ret)
234		return ret;
235
236	queue_work(system_long_wq, &drv->boot_work);
237
238	return 0;
239}
240
241static int class_runtime_suspend(struct device *dev)
242{
243	struct sdca_class_drv *drv = dev_get_drvdata(dev);
244
245	/*
246	 * Whilst the driver doesn't power the chip down here, going into runtime
247	 * suspend lets the SoundWire bus power down, which means the driver
248	 * can't communicate with the device any more.
249	 */
250	regcache_cache_only(drv->dev_regmap, true);
251
252	return 0;
253}
254
255static int class_runtime_resume(struct device *dev)
256{
257	struct sdca_class_drv *drv = dev_get_drvdata(dev);
258	int ret;
259
260	ret = class_wait_for_attach(drv);
261	if (ret)
262		goto err;
263
264	regcache_mark_dirty(drv->dev_regmap);
265
266	ret = regcache_sync(drv->dev_regmap);
267	if (ret) {
268		dev_err(drv->dev, "failed to restore cache: %d\n", ret);
269		goto err;
270	}
271
272	return 0;
273
274err:
275	regcache_cache_only(drv->dev_regmap, true);
276
277	return ret;
278}
279
280static const struct dev_pm_ops class_pm_ops = {
281	RUNTIME_PM_OPS(class_runtime_suspend, class_runtime_resume, NULL)
282};
283
284static const struct sdw_device_id class_sdw_id[] = {
285	SDW_SLAVE_ENTRY(0x01FA, 0x4245, 0),
286	{}
287};
288MODULE_DEVICE_TABLE(sdw, class_sdw_id);
289
290static struct sdw_driver class_sdw_driver = {
291	.driver = {
292		.name		= "sdca_class",
293		.pm		= pm_ptr(&class_pm_ops),
294	},
295
296	.probe		= class_sdw_probe,
297	.id_table	= class_sdw_id,
298	.ops		= &class_sdw_ops,
299};
300module_sdw_driver(class_sdw_driver);
301
302MODULE_LICENSE("GPL");
303MODULE_DESCRIPTION("SDCA Class Driver");
304MODULE_IMPORT_NS("SND_SOC_SDCA");
~/ Read-only file preview.