1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * CS42L43 SoundWire driver 4 * 5 * Copyright (C) 2022-2023 Cirrus Logic, Inc. and 6 * Cirrus Logic International Semiconductor Ltd. 7 */ 8 9 #include <linux/array_size.h> 10 #include <linux/device.h> 11 #include <linux/err.h> 12 #include <linux/mfd/cs42l43.h> 13 #include <linux/mfd/cs42l43-regs.h> 14 #include <linux/mod_devicetable.h> 15 #include <linux/module.h> 16 #include <linux/pm.h> 17 #include <linux/regmap.h> 18 #include <linux/soundwire/sdw.h> 19 #include <linux/soundwire/sdw_registers.h> 20 #include <linux/soundwire/sdw_type.h> 21 22 #include "cs42l43.h" 23 24 #define CS42L43_SDW_PORT(port, chans) { \ 25 .num = port, \ 26 .max_ch = chans, \ 27 .type = SDW_DPN_FULL, \ 28 .max_word = 24, \ 29 } 30 31 static const struct regmap_config cs42l43_sdw_regmap = { 32 .reg_bits = 32, 33 .reg_stride = 4, 34 .val_bits = 32, 35 .reg_format_endian = REGMAP_ENDIAN_LITTLE, 36 .val_format_endian = REGMAP_ENDIAN_LITTLE, 37 38 .max_register = CS42L43_MCU_RAM_MAX, 39 .readable_reg = cs42l43_readable_register, 40 .volatile_reg = cs42l43_volatile_register, 41 .precious_reg = cs42l43_precious_register, 42 43 .cache_type = REGCACHE_MAPLE, 44 .reg_defaults = cs42l43_reg_default, 45 .num_reg_defaults = ARRAY_SIZE(cs42l43_reg_default), 46 }; 47 48 static const struct sdw_dpn_prop cs42l43_src_port_props[] = { 49 CS42L43_SDW_PORT(1, 4), 50 CS42L43_SDW_PORT(2, 2), 51 CS42L43_SDW_PORT(3, 2), 52 CS42L43_SDW_PORT(4, 2), 53 }; 54 55 static const struct sdw_dpn_prop cs42l43_sink_port_props[] = { 56 CS42L43_SDW_PORT(5, 2), 57 CS42L43_SDW_PORT(6, 2), 58 CS42L43_SDW_PORT(7, 2), 59 }; 60 61 static int cs42l43_read_prop(struct sdw_slave *sdw) 62 { 63 struct sdw_slave_prop *prop = &sdw->prop; 64 struct device *dev = &sdw->dev; 65 int i; 66 67 prop->use_domain_irq = true; 68 prop->paging_support = true; 69 prop->wake_capable = true; 70 prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; 71 prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | 72 SDW_SCP_INT1_IMPL_DEF; 73 74 for (i = 0; i < ARRAY_SIZE(cs42l43_src_port_props); i++) 75 prop->source_ports |= BIT(cs42l43_src_port_props[i].num); 76 77 prop->src_dpn_prop = devm_kmemdup(dev, cs42l43_src_port_props, 78 sizeof(cs42l43_src_port_props), GFP_KERNEL); 79 if (!prop->src_dpn_prop) 80 return -ENOMEM; 81 82 for (i = 0; i < ARRAY_SIZE(cs42l43_sink_port_props); i++) 83 prop->sink_ports |= BIT(cs42l43_sink_port_props[i].num); 84 85 prop->sink_dpn_prop = devm_kmemdup(dev, cs42l43_sink_port_props, 86 sizeof(cs42l43_sink_port_props), GFP_KERNEL); 87 if (!prop->sink_dpn_prop) 88 return -ENOMEM; 89 90 return 0; 91 } 92 93 static int cs42l43_sdw_update_status(struct sdw_slave *sdw, enum sdw_slave_status status) 94 { 95 struct cs42l43 *cs42l43 = dev_get_drvdata(&sdw->dev); 96 97 switch (status) { 98 case SDW_SLAVE_ATTACHED: 99 dev_dbg(cs42l43->dev, "Device attach\n"); 100 101 sdw_write_no_pm(sdw, CS42L43_GEN_INT_MASK_1, 102 CS42L43_INT_STAT_GEN1_MASK); 103 104 cs42l43->attached = true; 105 106 complete(&cs42l43->device_attach); 107 break; 108 case SDW_SLAVE_UNATTACHED: 109 dev_dbg(cs42l43->dev, "Device detach\n"); 110 111 cs42l43->attached = false; 112 113 reinit_completion(&cs42l43->device_attach); 114 complete(&cs42l43->device_detach); 115 break; 116 default: 117 break; 118 } 119 120 return 0; 121 } 122 123 static int cs42l43_sdw_interrupt(struct sdw_slave *sdw, 124 struct sdw_slave_intr_status *status) 125 { 126 /* 127 * The IRQ itself was handled through the regmap_irq handler, this is 128 * just clearing up the additional Cirrus SoundWire registers that are 129 * not covered by the SoundWire framework or the IRQ handler itself. 130 * There is only a single bit in GEN_INT_STAT_1 and it doesn't clear if 131 * IRQs are still pending so doing a read/write here after handling the 132 * IRQ is fine. 133 */ 134 sdw_read_no_pm(sdw, CS42L43_GEN_INT_STAT_1); 135 sdw_write_no_pm(sdw, CS42L43_GEN_INT_STAT_1, CS42L43_INT_STAT_GEN1_MASK); 136 137 return 0; 138 } 139 140 static int cs42l43_sdw_bus_config(struct sdw_slave *sdw, 141 struct sdw_bus_params *params) 142 { 143 struct cs42l43 *cs42l43 = dev_get_drvdata(&sdw->dev); 144 int ret = 0; 145 146 mutex_lock(&cs42l43->pll_lock); 147 148 if (cs42l43->sdw_freq != params->curr_dr_freq / 2) { 149 if (cs42l43->sdw_pll_active) { 150 dev_err(cs42l43->dev, 151 "PLL active can't change SoundWire bus clock\n"); 152 ret = -EBUSY; 153 } else { 154 cs42l43->sdw_freq = params->curr_dr_freq / 2; 155 } 156 } 157 158 mutex_unlock(&cs42l43->pll_lock); 159 160 return ret; 161 } 162 163 static const struct sdw_slave_ops cs42l43_sdw_ops = { 164 .read_prop = cs42l43_read_prop, 165 .update_status = cs42l43_sdw_update_status, 166 .interrupt_callback = cs42l43_sdw_interrupt, 167 .bus_config = cs42l43_sdw_bus_config, 168 }; 169 170 static int cs42l43_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id) 171 { 172 struct cs42l43 *cs42l43; 173 struct device *dev = &sdw->dev; 174 175 cs42l43 = devm_kzalloc(dev, sizeof(*cs42l43), GFP_KERNEL); 176 if (!cs42l43) 177 return -ENOMEM; 178 179 cs42l43->dev = dev; 180 cs42l43->sdw = sdw; 181 182 cs42l43->regmap = devm_regmap_init_sdw(sdw, &cs42l43_sdw_regmap); 183 if (IS_ERR(cs42l43->regmap)) 184 return dev_err_probe(cs42l43->dev, PTR_ERR(cs42l43->regmap), 185 "Failed to allocate regmap\n"); 186 187 return cs42l43_dev_probe(cs42l43); 188 } 189 190 static int cs42l43_sdw_remove(struct sdw_slave *sdw) 191 { 192 struct cs42l43 *cs42l43 = dev_get_drvdata(&sdw->dev); 193 194 cs42l43_dev_remove(cs42l43); 195 196 return 0; 197 } 198 199 static const struct sdw_device_id cs42l43_sdw_id[] = { 200 SDW_SLAVE_ENTRY(0x01FA, 0x4243, 0), 201 {} 202 }; 203 MODULE_DEVICE_TABLE(sdw, cs42l43_sdw_id); 204 205 static struct sdw_driver cs42l43_sdw_driver = { 206 .driver = { 207 .name = "cs42l43", 208 .pm = pm_ptr(&cs42l43_pm_ops), 209 }, 210 211 .probe = cs42l43_sdw_probe, 212 .remove = cs42l43_sdw_remove, 213 .id_table = cs42l43_sdw_id, 214 .ops = &cs42l43_sdw_ops, 215 }; 216 module_sdw_driver(cs42l43_sdw_driver); 217 218 MODULE_IMPORT_NS(MFD_CS42L43); 219 220 MODULE_DESCRIPTION("CS42L43 SoundWire Driver"); 221 MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>"); 222 MODULE_LICENSE("GPL"); 223