Jack Yu | 20d1705 | 2021-03-02 18:30:42 +0800 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | // |
| 3 | // rt715-sdca-sdw.c -- rt715 ALSA SoC audio driver |
| 4 | // |
| 5 | // Copyright(c) 2020 Realtek Semiconductor Corp. |
| 6 | // |
| 7 | // |
| 8 | |
| 9 | #include <linux/delay.h> |
| 10 | #include <linux/device.h> |
| 11 | #include <linux/mod_devicetable.h> |
| 12 | #include <linux/soundwire/sdw.h> |
| 13 | #include <linux/soundwire/sdw_type.h> |
| 14 | #include <linux/soundwire/sdw_registers.h> |
| 15 | #include <linux/module.h> |
| 16 | #include <linux/regmap.h> |
| 17 | #include <sound/soc.h> |
| 18 | #include "rt715-sdca.h" |
| 19 | #include "rt715-sdca-sdw.h" |
| 20 | |
| 21 | static bool rt715_sdca_readable_register(struct device *dev, unsigned int reg) |
| 22 | { |
| 23 | switch (reg) { |
| 24 | case 0x201a ... 0x2027: |
| 25 | case 0x2029 ... 0x202a: |
| 26 | case 0x202d ... 0x2034: |
| 27 | case 0x2200 ... 0x2204: |
| 28 | case 0x2206 ... 0x2212: |
| 29 | case 0x2230 ... 0x2239: |
| 30 | case 0x2f5b: |
| 31 | case SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, |
| 32 | RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00): |
| 33 | return true; |
| 34 | default: |
| 35 | return false; |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | static bool rt715_sdca_volatile_register(struct device *dev, unsigned int reg) |
| 40 | { |
| 41 | switch (reg) { |
| 42 | case 0x201b: |
| 43 | case 0x201c: |
| 44 | case 0x201d: |
| 45 | case 0x201f: |
| 46 | case 0x2021: |
| 47 | case 0x2023: |
| 48 | case 0x2230: |
| 49 | case 0x202d ... 0x202f: /* BRA */ |
| 50 | case 0x2200 ... 0x2212: /* i2c debug */ |
| 51 | case 0x2f07: |
| 52 | case 0x2f1b ... 0x2f1e: |
| 53 | case 0x2f30 ... 0x2f34: |
| 54 | case 0x2f50 ... 0x2f51: |
| 55 | case 0x2f53 ... 0x2f59: |
| 56 | case 0x2f5c ... 0x2f5f: |
| 57 | case SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, |
| 58 | RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00): /* VAD Searching status */ |
| 59 | return true; |
| 60 | default: |
| 61 | return false; |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | static bool rt715_sdca_mbq_readable_register(struct device *dev, unsigned int reg) |
| 66 | { |
| 67 | switch (reg) { |
| 68 | case 0x2000000: |
| 69 | case 0x200002b: |
| 70 | case 0x2000036: |
| 71 | case 0x2000037: |
| 72 | case 0x2000039: |
Jack Yu | e343d34a | 2021-06-07 17:22:35 -0500 | [diff] [blame] | 73 | case 0x2000044: |
Jack Yu | 20d1705 | 2021-03-02 18:30:42 +0800 | [diff] [blame] | 74 | case 0x6100000: |
| 75 | return true; |
| 76 | default: |
| 77 | return false; |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | static bool rt715_sdca_mbq_volatile_register(struct device *dev, unsigned int reg) |
| 82 | { |
| 83 | switch (reg) { |
| 84 | case 0x2000000: |
| 85 | return true; |
| 86 | default: |
| 87 | return false; |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | static const struct regmap_config rt715_sdca_regmap = { |
| 92 | .reg_bits = 32, |
| 93 | .val_bits = 8, |
| 94 | .readable_reg = rt715_sdca_readable_register, |
| 95 | .volatile_reg = rt715_sdca_volatile_register, |
| 96 | .max_register = 0x43ffffff, |
| 97 | .reg_defaults = rt715_reg_defaults_sdca, |
| 98 | .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults_sdca), |
| 99 | .cache_type = REGCACHE_RBTREE, |
| 100 | .use_single_read = true, |
| 101 | .use_single_write = true, |
| 102 | }; |
| 103 | |
| 104 | static const struct regmap_config rt715_sdca_mbq_regmap = { |
| 105 | .name = "sdw-mbq", |
| 106 | .reg_bits = 32, |
| 107 | .val_bits = 16, |
| 108 | .readable_reg = rt715_sdca_mbq_readable_register, |
| 109 | .volatile_reg = rt715_sdca_mbq_volatile_register, |
| 110 | .max_register = 0x43ffffff, |
| 111 | .reg_defaults = rt715_mbq_reg_defaults_sdca, |
| 112 | .num_reg_defaults = ARRAY_SIZE(rt715_mbq_reg_defaults_sdca), |
| 113 | .cache_type = REGCACHE_RBTREE, |
| 114 | .use_single_read = true, |
| 115 | .use_single_write = true, |
| 116 | }; |
| 117 | |
| 118 | static int rt715_sdca_update_status(struct sdw_slave *slave, |
| 119 | enum sdw_slave_status status) |
| 120 | { |
| 121 | struct rt715_sdca_priv *rt715 = dev_get_drvdata(&slave->dev); |
| 122 | |
| 123 | /* Update the status */ |
| 124 | rt715->status = status; |
| 125 | |
| 126 | /* |
| 127 | * Perform initialization only if slave status is present and |
| 128 | * hw_init flag is false |
| 129 | */ |
| 130 | if (rt715->hw_init || rt715->status != SDW_SLAVE_ATTACHED) |
| 131 | return 0; |
| 132 | |
| 133 | /* perform I/O transfers required for Slave initialization */ |
| 134 | return rt715_sdca_io_init(&slave->dev, slave); |
| 135 | } |
| 136 | |
| 137 | static int rt715_sdca_read_prop(struct sdw_slave *slave) |
| 138 | { |
| 139 | struct sdw_slave_prop *prop = &slave->prop; |
| 140 | int nval, i; |
| 141 | u32 bit; |
| 142 | unsigned long addr; |
| 143 | struct sdw_dpn_prop *dpn; |
| 144 | |
| 145 | prop->paging_support = true; |
| 146 | |
| 147 | /* first we need to allocate memory for set bits in port lists */ |
| 148 | prop->source_ports = 0x50;/* BITMAP: 01010000 */ |
| 149 | prop->sink_ports = 0x0; /* BITMAP: 00000000 */ |
| 150 | |
| 151 | nval = hweight32(prop->source_ports); |
| 152 | prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, |
| 153 | sizeof(*prop->src_dpn_prop), |
| 154 | GFP_KERNEL); |
| 155 | if (!prop->src_dpn_prop) |
| 156 | return -ENOMEM; |
| 157 | |
| 158 | dpn = prop->src_dpn_prop; |
| 159 | i = 0; |
| 160 | addr = prop->source_ports; |
| 161 | for_each_set_bit(bit, &addr, 32) { |
| 162 | dpn[i].num = bit; |
| 163 | dpn[i].simple_ch_prep_sm = true; |
| 164 | dpn[i].ch_prep_timeout = 10; |
| 165 | i++; |
| 166 | } |
| 167 | |
| 168 | /* set the timeout values */ |
| 169 | prop->clk_stop_timeout = 20; |
| 170 | |
| 171 | return 0; |
| 172 | } |
| 173 | |
| 174 | static struct sdw_slave_ops rt715_sdca_slave_ops = { |
| 175 | .read_prop = rt715_sdca_read_prop, |
| 176 | .update_status = rt715_sdca_update_status, |
| 177 | }; |
| 178 | |
| 179 | static int rt715_sdca_sdw_probe(struct sdw_slave *slave, |
| 180 | const struct sdw_device_id *id) |
| 181 | { |
| 182 | struct regmap *mbq_regmap, *regmap; |
| 183 | |
| 184 | slave->ops = &rt715_sdca_slave_ops; |
| 185 | |
| 186 | /* Regmap Initialization */ |
| 187 | mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt715_sdca_mbq_regmap); |
Wei Yongjun | d322360 | 2021-03-09 13:14:58 +0000 | [diff] [blame] | 188 | if (IS_ERR(mbq_regmap)) |
| 189 | return PTR_ERR(mbq_regmap); |
Jack Yu | 20d1705 | 2021-03-02 18:30:42 +0800 | [diff] [blame] | 190 | |
| 191 | regmap = devm_regmap_init_sdw(slave, &rt715_sdca_regmap); |
Wei Yongjun | d322360 | 2021-03-09 13:14:58 +0000 | [diff] [blame] | 192 | if (IS_ERR(regmap)) |
| 193 | return PTR_ERR(regmap); |
Jack Yu | 20d1705 | 2021-03-02 18:30:42 +0800 | [diff] [blame] | 194 | |
| 195 | return rt715_sdca_init(&slave->dev, mbq_regmap, regmap, slave); |
| 196 | } |
| 197 | |
| 198 | static const struct sdw_device_id rt715_sdca_id[] = { |
| 199 | SDW_SLAVE_ENTRY_EXT(0x025d, 0x715, 0x3, 0x1, 0), |
| 200 | SDW_SLAVE_ENTRY_EXT(0x025d, 0x714, 0x3, 0x1, 0), |
| 201 | {}, |
| 202 | }; |
| 203 | MODULE_DEVICE_TABLE(sdw, rt715_sdca_id); |
| 204 | |
| 205 | static int __maybe_unused rt715_dev_suspend(struct device *dev) |
| 206 | { |
| 207 | struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev); |
| 208 | |
| 209 | if (!rt715->hw_init) |
| 210 | return 0; |
| 211 | |
| 212 | regcache_cache_only(rt715->regmap, true); |
| 213 | regcache_mark_dirty(rt715->regmap); |
| 214 | regcache_cache_only(rt715->mbq_regmap, true); |
| 215 | regcache_mark_dirty(rt715->mbq_regmap); |
| 216 | |
| 217 | return 0; |
| 218 | } |
| 219 | |
| 220 | #define RT715_PROBE_TIMEOUT 5000 |
| 221 | |
| 222 | static int __maybe_unused rt715_dev_resume(struct device *dev) |
| 223 | { |
| 224 | struct sdw_slave *slave = dev_to_sdw_dev(dev); |
| 225 | struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev); |
| 226 | unsigned long time; |
| 227 | |
Pierre-Louis Bossart | d34d089 | 2021-06-07 17:22:33 -0500 | [diff] [blame] | 228 | if (!rt715->first_hw_init) |
Jack Yu | 20d1705 | 2021-03-02 18:30:42 +0800 | [diff] [blame] | 229 | return 0; |
| 230 | |
| 231 | if (!slave->unattach_request) |
| 232 | goto regmap_sync; |
| 233 | |
| 234 | time = wait_for_completion_timeout(&slave->enumeration_complete, |
| 235 | msecs_to_jiffies(RT715_PROBE_TIMEOUT)); |
| 236 | if (!time) { |
| 237 | dev_err(&slave->dev, "Enumeration not complete, timed out\n"); |
| 238 | return -ETIMEDOUT; |
| 239 | } |
| 240 | |
| 241 | regmap_sync: |
| 242 | slave->unattach_request = 0; |
| 243 | regcache_cache_only(rt715->regmap, false); |
| 244 | regcache_sync_region(rt715->regmap, |
| 245 | SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL, |
| 246 | CH_00), |
| 247 | SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, |
| 248 | RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00)); |
| 249 | regcache_cache_only(rt715->mbq_regmap, false); |
| 250 | regcache_sync_region(rt715->mbq_regmap, 0x2000000, 0x61020ff); |
| 251 | regcache_sync_region(rt715->mbq_regmap, |
| 252 | SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL, |
| 253 | CH_00), |
| 254 | SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, |
| 255 | RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00)); |
| 256 | |
| 257 | return 0; |
| 258 | } |
| 259 | |
| 260 | static const struct dev_pm_ops rt715_pm = { |
| 261 | SET_SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume) |
| 262 | SET_RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL) |
| 263 | }; |
| 264 | |
| 265 | static struct sdw_driver rt715_sdw_driver = { |
| 266 | .driver = { |
| 267 | .name = "rt715-sdca", |
| 268 | .owner = THIS_MODULE, |
| 269 | .pm = &rt715_pm, |
| 270 | }, |
| 271 | .probe = rt715_sdca_sdw_probe, |
| 272 | .ops = &rt715_sdca_slave_ops, |
| 273 | .id_table = rt715_sdca_id, |
| 274 | }; |
| 275 | module_sdw_driver(rt715_sdw_driver); |
| 276 | |
| 277 | MODULE_DESCRIPTION("ASoC RT715 driver SDW SDCA"); |
| 278 | MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>"); |
| 279 | MODULE_LICENSE("GPL v2"); |