Sanyog Kale | 89e5905 | 2018-04-26 18:38:08 +0530 | [diff] [blame] | 1 | // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) |
| 2 | // Copyright(c) 2015-18 Intel Corporation. |
| 3 | |
| 4 | /* |
| 5 | * stream.c - SoundWire Bus stream operations. |
| 6 | */ |
| 7 | |
| 8 | #include <linux/delay.h> |
| 9 | #include <linux/device.h> |
| 10 | #include <linux/init.h> |
| 11 | #include <linux/module.h> |
| 12 | #include <linux/mod_devicetable.h> |
| 13 | #include <linux/slab.h> |
| 14 | #include <linux/soundwire/sdw.h> |
| 15 | #include "bus.h" |
| 16 | |
| 17 | /** |
| 18 | * sdw_release_stream() - Free the assigned stream runtime |
| 19 | * |
| 20 | * @stream: SoundWire stream runtime |
| 21 | * |
| 22 | * sdw_release_stream should be called only once per stream |
| 23 | */ |
| 24 | void sdw_release_stream(struct sdw_stream_runtime *stream) |
| 25 | { |
| 26 | kfree(stream); |
| 27 | } |
| 28 | EXPORT_SYMBOL(sdw_release_stream); |
| 29 | |
| 30 | /** |
| 31 | * sdw_alloc_stream() - Allocate and return stream runtime |
| 32 | * |
| 33 | * @stream_name: SoundWire stream name |
| 34 | * |
| 35 | * Allocates a SoundWire stream runtime instance. |
| 36 | * sdw_alloc_stream should be called only once per stream. Typically |
| 37 | * invoked from ALSA/ASoC machine/platform driver. |
| 38 | */ |
| 39 | struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name) |
| 40 | { |
| 41 | struct sdw_stream_runtime *stream; |
| 42 | |
| 43 | stream = kzalloc(sizeof(*stream), GFP_KERNEL); |
| 44 | if (!stream) |
| 45 | return NULL; |
| 46 | |
| 47 | stream->name = stream_name; |
| 48 | stream->state = SDW_STREAM_ALLOCATED; |
| 49 | |
| 50 | return stream; |
| 51 | } |
| 52 | EXPORT_SYMBOL(sdw_alloc_stream); |
| 53 | |
| 54 | /** |
| 55 | * sdw_alloc_master_rt() - Allocates and initialize Master runtime handle |
| 56 | * |
| 57 | * @bus: SDW bus instance |
| 58 | * @stream_config: Stream configuration |
| 59 | * @stream: Stream runtime handle. |
| 60 | * |
| 61 | * This function is to be called with bus_lock held. |
| 62 | */ |
| 63 | static struct sdw_master_runtime |
| 64 | *sdw_alloc_master_rt(struct sdw_bus *bus, |
| 65 | struct sdw_stream_config *stream_config, |
| 66 | struct sdw_stream_runtime *stream) |
| 67 | { |
| 68 | struct sdw_master_runtime *m_rt; |
| 69 | |
| 70 | m_rt = stream->m_rt; |
| 71 | |
| 72 | /* |
| 73 | * check if Master is already allocated (as a result of Slave adding |
| 74 | * it first), if so skip allocation and go to configure |
| 75 | */ |
| 76 | if (m_rt) |
| 77 | goto stream_config; |
| 78 | |
| 79 | m_rt = kzalloc(sizeof(*m_rt), GFP_KERNEL); |
| 80 | if (!m_rt) |
| 81 | return NULL; |
| 82 | |
| 83 | /* Initialization of Master runtime handle */ |
Sanyog Kale | bbe7379 | 2018-04-26 18:38:13 +0530 | [diff] [blame^] | 84 | INIT_LIST_HEAD(&m_rt->port_list); |
Sanyog Kale | 89e5905 | 2018-04-26 18:38:08 +0530 | [diff] [blame] | 85 | INIT_LIST_HEAD(&m_rt->slave_rt_list); |
| 86 | stream->m_rt = m_rt; |
| 87 | |
| 88 | list_add_tail(&m_rt->bus_node, &bus->m_rt_list); |
| 89 | |
| 90 | stream_config: |
| 91 | m_rt->ch_count = stream_config->ch_count; |
| 92 | m_rt->bus = bus; |
| 93 | m_rt->stream = stream; |
| 94 | m_rt->direction = stream_config->direction; |
| 95 | |
| 96 | return m_rt; |
| 97 | } |
| 98 | |
| 99 | /** |
| 100 | * sdw_alloc_slave_rt() - Allocate and initialize Slave runtime handle. |
| 101 | * |
| 102 | * @slave: Slave handle |
| 103 | * @stream_config: Stream configuration |
| 104 | * @stream: Stream runtime handle |
| 105 | * |
| 106 | * This function is to be called with bus_lock held. |
| 107 | */ |
| 108 | static struct sdw_slave_runtime |
| 109 | *sdw_alloc_slave_rt(struct sdw_slave *slave, |
| 110 | struct sdw_stream_config *stream_config, |
| 111 | struct sdw_stream_runtime *stream) |
| 112 | { |
| 113 | struct sdw_slave_runtime *s_rt = NULL; |
| 114 | |
| 115 | s_rt = kzalloc(sizeof(*s_rt), GFP_KERNEL); |
| 116 | if (!s_rt) |
| 117 | return NULL; |
| 118 | |
Sanyog Kale | bbe7379 | 2018-04-26 18:38:13 +0530 | [diff] [blame^] | 119 | INIT_LIST_HEAD(&s_rt->port_list); |
Sanyog Kale | 89e5905 | 2018-04-26 18:38:08 +0530 | [diff] [blame] | 120 | s_rt->ch_count = stream_config->ch_count; |
| 121 | s_rt->direction = stream_config->direction; |
| 122 | s_rt->slave = slave; |
| 123 | |
| 124 | return s_rt; |
| 125 | } |
| 126 | |
Sanyog Kale | bbe7379 | 2018-04-26 18:38:13 +0530 | [diff] [blame^] | 127 | static void sdw_master_port_release(struct sdw_bus *bus, |
| 128 | struct sdw_master_runtime *m_rt) |
| 129 | { |
| 130 | struct sdw_port_runtime *p_rt, *_p_rt; |
| 131 | |
| 132 | list_for_each_entry_safe(p_rt, _p_rt, |
| 133 | &m_rt->port_list, port_node) { |
| 134 | list_del(&p_rt->port_node); |
| 135 | kfree(p_rt); |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | static void sdw_slave_port_release(struct sdw_bus *bus, |
| 140 | struct sdw_slave *slave, |
| 141 | struct sdw_stream_runtime *stream) |
| 142 | { |
| 143 | struct sdw_port_runtime *p_rt, *_p_rt; |
| 144 | struct sdw_master_runtime *m_rt = stream->m_rt; |
| 145 | struct sdw_slave_runtime *s_rt; |
| 146 | |
| 147 | list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { |
| 148 | if (s_rt->slave != slave) |
| 149 | continue; |
| 150 | |
| 151 | list_for_each_entry_safe(p_rt, _p_rt, |
| 152 | &s_rt->port_list, port_node) { |
| 153 | list_del(&p_rt->port_node); |
| 154 | kfree(p_rt); |
| 155 | } |
| 156 | } |
| 157 | } |
| 158 | |
Sanyog Kale | 89e5905 | 2018-04-26 18:38:08 +0530 | [diff] [blame] | 159 | /** |
| 160 | * sdw_release_slave_stream() - Free Slave(s) runtime handle |
| 161 | * |
| 162 | * @slave: Slave handle. |
| 163 | * @stream: Stream runtime handle. |
| 164 | * |
| 165 | * This function is to be called with bus_lock held. |
| 166 | */ |
| 167 | static void sdw_release_slave_stream(struct sdw_slave *slave, |
| 168 | struct sdw_stream_runtime *stream) |
| 169 | { |
| 170 | struct sdw_slave_runtime *s_rt, *_s_rt; |
| 171 | struct sdw_master_runtime *m_rt = stream->m_rt; |
| 172 | |
| 173 | /* Retrieve Slave runtime handle */ |
| 174 | list_for_each_entry_safe(s_rt, _s_rt, |
| 175 | &m_rt->slave_rt_list, m_rt_node) { |
| 176 | |
| 177 | if (s_rt->slave == slave) { |
| 178 | list_del(&s_rt->m_rt_node); |
| 179 | kfree(s_rt); |
| 180 | return; |
| 181 | } |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | /** |
| 186 | * sdw_release_master_stream() - Free Master runtime handle |
| 187 | * |
| 188 | * @stream: Stream runtime handle. |
| 189 | * |
| 190 | * This function is to be called with bus_lock held |
| 191 | * It frees the Master runtime handle and associated Slave(s) runtime |
| 192 | * handle. If this is called first then sdw_release_slave_stream() will have |
| 193 | * no effect as Slave(s) runtime handle would already be freed up. |
| 194 | */ |
| 195 | static void sdw_release_master_stream(struct sdw_stream_runtime *stream) |
| 196 | { |
| 197 | struct sdw_master_runtime *m_rt = stream->m_rt; |
| 198 | struct sdw_slave_runtime *s_rt, *_s_rt; |
| 199 | |
| 200 | list_for_each_entry_safe(s_rt, _s_rt, |
| 201 | &m_rt->slave_rt_list, m_rt_node) |
| 202 | sdw_stream_remove_slave(s_rt->slave, stream); |
| 203 | |
| 204 | list_del(&m_rt->bus_node); |
| 205 | } |
| 206 | |
| 207 | /** |
| 208 | * sdw_stream_remove_master() - Remove master from sdw_stream |
| 209 | * |
| 210 | * @bus: SDW Bus instance |
| 211 | * @stream: SoundWire stream |
| 212 | * |
Sanyog Kale | bbe7379 | 2018-04-26 18:38:13 +0530 | [diff] [blame^] | 213 | * This removes and frees port_rt and master_rt from a stream |
Sanyog Kale | 89e5905 | 2018-04-26 18:38:08 +0530 | [diff] [blame] | 214 | */ |
| 215 | int sdw_stream_remove_master(struct sdw_bus *bus, |
| 216 | struct sdw_stream_runtime *stream) |
| 217 | { |
| 218 | mutex_lock(&bus->bus_lock); |
| 219 | |
| 220 | sdw_release_master_stream(stream); |
Sanyog Kale | bbe7379 | 2018-04-26 18:38:13 +0530 | [diff] [blame^] | 221 | sdw_master_port_release(bus, stream->m_rt); |
Sanyog Kale | 89e5905 | 2018-04-26 18:38:08 +0530 | [diff] [blame] | 222 | stream->state = SDW_STREAM_RELEASED; |
| 223 | kfree(stream->m_rt); |
| 224 | stream->m_rt = NULL; |
| 225 | |
| 226 | mutex_unlock(&bus->bus_lock); |
| 227 | |
| 228 | return 0; |
| 229 | } |
| 230 | EXPORT_SYMBOL(sdw_stream_remove_master); |
| 231 | |
| 232 | /** |
| 233 | * sdw_stream_remove_slave() - Remove slave from sdw_stream |
| 234 | * |
| 235 | * @slave: SDW Slave instance |
| 236 | * @stream: SoundWire stream |
| 237 | * |
Sanyog Kale | bbe7379 | 2018-04-26 18:38:13 +0530 | [diff] [blame^] | 238 | * This removes and frees port_rt and slave_rt from a stream |
Sanyog Kale | 89e5905 | 2018-04-26 18:38:08 +0530 | [diff] [blame] | 239 | */ |
| 240 | int sdw_stream_remove_slave(struct sdw_slave *slave, |
| 241 | struct sdw_stream_runtime *stream) |
| 242 | { |
| 243 | mutex_lock(&slave->bus->bus_lock); |
| 244 | |
Sanyog Kale | bbe7379 | 2018-04-26 18:38:13 +0530 | [diff] [blame^] | 245 | sdw_slave_port_release(slave->bus, slave, stream); |
Sanyog Kale | 89e5905 | 2018-04-26 18:38:08 +0530 | [diff] [blame] | 246 | sdw_release_slave_stream(slave, stream); |
| 247 | |
| 248 | mutex_unlock(&slave->bus->bus_lock); |
| 249 | |
| 250 | return 0; |
| 251 | } |
| 252 | EXPORT_SYMBOL(sdw_stream_remove_slave); |
| 253 | |
| 254 | /** |
| 255 | * sdw_config_stream() - Configure the allocated stream |
| 256 | * |
| 257 | * @dev: SDW device |
| 258 | * @stream: SoundWire stream |
| 259 | * @stream_config: Stream configuration for audio stream |
| 260 | * @is_slave: is API called from Slave or Master |
| 261 | * |
| 262 | * This function is to be called with bus_lock held. |
| 263 | */ |
| 264 | static int sdw_config_stream(struct device *dev, |
| 265 | struct sdw_stream_runtime *stream, |
| 266 | struct sdw_stream_config *stream_config, bool is_slave) |
| 267 | { |
| 268 | /* |
| 269 | * Update the stream rate, channel and bps based on data |
| 270 | * source. For more than one data source (multilink), |
| 271 | * match the rate, bps, stream type and increment number of channels. |
| 272 | * |
| 273 | * If rate/bps is zero, it means the values are not set, so skip |
| 274 | * comparison and allow the value to be set and stored in stream |
| 275 | */ |
| 276 | if (stream->params.rate && |
| 277 | stream->params.rate != stream_config->frame_rate) { |
| 278 | dev_err(dev, "rate not matching, stream:%s", stream->name); |
| 279 | return -EINVAL; |
| 280 | } |
| 281 | |
| 282 | if (stream->params.bps && |
| 283 | stream->params.bps != stream_config->bps) { |
| 284 | dev_err(dev, "bps not matching, stream:%s", stream->name); |
| 285 | return -EINVAL; |
| 286 | } |
| 287 | |
| 288 | stream->type = stream_config->type; |
| 289 | stream->params.rate = stream_config->frame_rate; |
| 290 | stream->params.bps = stream_config->bps; |
| 291 | |
| 292 | /* TODO: Update this check during Device-device support */ |
| 293 | if (is_slave) |
| 294 | stream->params.ch_count += stream_config->ch_count; |
| 295 | |
| 296 | return 0; |
| 297 | } |
| 298 | |
Sanyog Kale | bbe7379 | 2018-04-26 18:38:13 +0530 | [diff] [blame^] | 299 | static int sdw_is_valid_port_range(struct device *dev, |
| 300 | struct sdw_port_runtime *p_rt) |
| 301 | { |
| 302 | if (!SDW_VALID_PORT_RANGE(p_rt->num)) { |
| 303 | dev_err(dev, |
| 304 | "SoundWire: Invalid port number :%d", p_rt->num); |
| 305 | return -EINVAL; |
| 306 | } |
| 307 | |
| 308 | return 0; |
| 309 | } |
| 310 | |
| 311 | static struct sdw_port_runtime *sdw_port_alloc(struct device *dev, |
| 312 | struct sdw_port_config *port_config, |
| 313 | int port_index) |
| 314 | { |
| 315 | struct sdw_port_runtime *p_rt; |
| 316 | |
| 317 | p_rt = kzalloc(sizeof(*p_rt), GFP_KERNEL); |
| 318 | if (!p_rt) |
| 319 | return NULL; |
| 320 | |
| 321 | p_rt->ch_mask = port_config[port_index].ch_mask; |
| 322 | p_rt->num = port_config[port_index].num; |
| 323 | |
| 324 | return p_rt; |
| 325 | } |
| 326 | |
| 327 | static int sdw_master_port_config(struct sdw_bus *bus, |
| 328 | struct sdw_master_runtime *m_rt, |
| 329 | struct sdw_port_config *port_config, |
| 330 | unsigned int num_ports) |
| 331 | { |
| 332 | struct sdw_port_runtime *p_rt; |
| 333 | int i; |
| 334 | |
| 335 | /* Iterate for number of ports to perform initialization */ |
| 336 | for (i = 0; i < num_ports; i++) { |
| 337 | p_rt = sdw_port_alloc(bus->dev, port_config, i); |
| 338 | if (!p_rt) |
| 339 | return -ENOMEM; |
| 340 | |
| 341 | /* |
| 342 | * TODO: Check port capabilities for requested |
| 343 | * configuration (audio mode support) |
| 344 | */ |
| 345 | |
| 346 | list_add_tail(&p_rt->port_node, &m_rt->port_list); |
| 347 | } |
| 348 | |
| 349 | return 0; |
| 350 | } |
| 351 | |
| 352 | static int sdw_slave_port_config(struct sdw_slave *slave, |
| 353 | struct sdw_slave_runtime *s_rt, |
| 354 | struct sdw_port_config *port_config, |
| 355 | unsigned int num_config) |
| 356 | { |
| 357 | struct sdw_port_runtime *p_rt; |
| 358 | int i, ret; |
| 359 | |
| 360 | /* Iterate for number of ports to perform initialization */ |
| 361 | for (i = 0; i < num_config; i++) { |
| 362 | p_rt = sdw_port_alloc(&slave->dev, port_config, i); |
| 363 | if (!p_rt) |
| 364 | return -ENOMEM; |
| 365 | |
| 366 | /* |
| 367 | * TODO: Check valid port range as defined by DisCo/ |
| 368 | * slave |
| 369 | */ |
| 370 | ret = sdw_is_valid_port_range(&slave->dev, p_rt); |
| 371 | if (ret < 0) { |
| 372 | kfree(p_rt); |
| 373 | return ret; |
| 374 | } |
| 375 | |
| 376 | /* |
| 377 | * TODO: Check port capabilities for requested |
| 378 | * configuration (audio mode support) |
| 379 | */ |
| 380 | |
| 381 | list_add_tail(&p_rt->port_node, &s_rt->port_list); |
| 382 | } |
| 383 | |
| 384 | return 0; |
| 385 | } |
| 386 | |
Sanyog Kale | 89e5905 | 2018-04-26 18:38:08 +0530 | [diff] [blame] | 387 | /** |
| 388 | * sdw_stream_add_master() - Allocate and add master runtime to a stream |
| 389 | * |
| 390 | * @bus: SDW Bus instance |
| 391 | * @stream_config: Stream configuration for audio stream |
Sanyog Kale | bbe7379 | 2018-04-26 18:38:13 +0530 | [diff] [blame^] | 392 | * @port_config: Port configuration for audio stream |
| 393 | * @num_ports: Number of ports |
Sanyog Kale | 89e5905 | 2018-04-26 18:38:08 +0530 | [diff] [blame] | 394 | * @stream: SoundWire stream |
| 395 | */ |
| 396 | int sdw_stream_add_master(struct sdw_bus *bus, |
| 397 | struct sdw_stream_config *stream_config, |
Sanyog Kale | bbe7379 | 2018-04-26 18:38:13 +0530 | [diff] [blame^] | 398 | struct sdw_port_config *port_config, |
| 399 | unsigned int num_ports, |
Sanyog Kale | 89e5905 | 2018-04-26 18:38:08 +0530 | [diff] [blame] | 400 | struct sdw_stream_runtime *stream) |
| 401 | { |
| 402 | struct sdw_master_runtime *m_rt = NULL; |
| 403 | int ret; |
| 404 | |
| 405 | mutex_lock(&bus->bus_lock); |
| 406 | |
| 407 | m_rt = sdw_alloc_master_rt(bus, stream_config, stream); |
| 408 | if (!m_rt) { |
| 409 | dev_err(bus->dev, |
| 410 | "Master runtime config failed for stream:%s", |
| 411 | stream->name); |
| 412 | ret = -ENOMEM; |
| 413 | goto error; |
| 414 | } |
| 415 | |
| 416 | ret = sdw_config_stream(bus->dev, stream, stream_config, false); |
| 417 | if (ret) |
| 418 | goto stream_error; |
| 419 | |
Sanyog Kale | bbe7379 | 2018-04-26 18:38:13 +0530 | [diff] [blame^] | 420 | ret = sdw_master_port_config(bus, m_rt, port_config, num_ports); |
| 421 | if (ret) |
| 422 | goto stream_error; |
| 423 | |
Sanyog Kale | 89e5905 | 2018-04-26 18:38:08 +0530 | [diff] [blame] | 424 | stream->state = SDW_STREAM_CONFIGURED; |
| 425 | |
| 426 | stream_error: |
| 427 | sdw_release_master_stream(stream); |
| 428 | error: |
| 429 | mutex_unlock(&bus->bus_lock); |
| 430 | return ret; |
| 431 | } |
| 432 | EXPORT_SYMBOL(sdw_stream_add_master); |
| 433 | |
| 434 | /** |
| 435 | * sdw_stream_add_slave() - Allocate and add master/slave runtime to a stream |
| 436 | * |
| 437 | * @slave: SDW Slave instance |
| 438 | * @stream_config: Stream configuration for audio stream |
| 439 | * @stream: SoundWire stream |
Sanyog Kale | bbe7379 | 2018-04-26 18:38:13 +0530 | [diff] [blame^] | 440 | * @port_config: Port configuration for audio stream |
| 441 | * @num_ports: Number of ports |
Sanyog Kale | 89e5905 | 2018-04-26 18:38:08 +0530 | [diff] [blame] | 442 | */ |
| 443 | int sdw_stream_add_slave(struct sdw_slave *slave, |
| 444 | struct sdw_stream_config *stream_config, |
Sanyog Kale | bbe7379 | 2018-04-26 18:38:13 +0530 | [diff] [blame^] | 445 | struct sdw_port_config *port_config, |
| 446 | unsigned int num_ports, |
Sanyog Kale | 89e5905 | 2018-04-26 18:38:08 +0530 | [diff] [blame] | 447 | struct sdw_stream_runtime *stream) |
| 448 | { |
| 449 | struct sdw_slave_runtime *s_rt; |
| 450 | struct sdw_master_runtime *m_rt; |
| 451 | int ret; |
| 452 | |
| 453 | mutex_lock(&slave->bus->bus_lock); |
| 454 | |
| 455 | /* |
| 456 | * If this API is invoked by Slave first then m_rt is not valid. |
| 457 | * So, allocate m_rt and add Slave to it. |
| 458 | */ |
| 459 | m_rt = sdw_alloc_master_rt(slave->bus, stream_config, stream); |
| 460 | if (!m_rt) { |
| 461 | dev_err(&slave->dev, |
| 462 | "alloc master runtime failed for stream:%s", |
| 463 | stream->name); |
| 464 | ret = -ENOMEM; |
| 465 | goto error; |
| 466 | } |
| 467 | |
| 468 | s_rt = sdw_alloc_slave_rt(slave, stream_config, stream); |
| 469 | if (!s_rt) { |
| 470 | dev_err(&slave->dev, |
| 471 | "Slave runtime config failed for stream:%s", |
| 472 | stream->name); |
| 473 | ret = -ENOMEM; |
| 474 | goto stream_error; |
| 475 | } |
| 476 | |
| 477 | ret = sdw_config_stream(&slave->dev, stream, stream_config, true); |
| 478 | if (ret) |
| 479 | goto stream_error; |
| 480 | |
| 481 | list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list); |
| 482 | |
Sanyog Kale | bbe7379 | 2018-04-26 18:38:13 +0530 | [diff] [blame^] | 483 | ret = sdw_slave_port_config(slave, s_rt, port_config, num_ports); |
| 484 | if (ret) |
| 485 | goto stream_error; |
| 486 | |
Sanyog Kale | 89e5905 | 2018-04-26 18:38:08 +0530 | [diff] [blame] | 487 | stream->state = SDW_STREAM_CONFIGURED; |
| 488 | goto error; |
| 489 | |
| 490 | stream_error: |
| 491 | /* |
| 492 | * we hit error so cleanup the stream, release all Slave(s) and |
| 493 | * Master runtime |
| 494 | */ |
| 495 | sdw_release_master_stream(stream); |
| 496 | error: |
| 497 | mutex_unlock(&slave->bus->bus_lock); |
| 498 | return ret; |
| 499 | } |
| 500 | EXPORT_SYMBOL(sdw_stream_add_slave); |