linux/drivers/staging/greybus/audio_module.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Greybus audio driver
   4 * Copyright 2015 Google Inc.
   5 * Copyright 2015 Linaro Ltd.
   6 */
   7#include <linux/kernel.h>
   8#include <linux/module.h>
   9#include <sound/soc.h>
  10#include <sound/pcm_params.h>
  11
  12#include "audio_codec.h"
  13#include "audio_apbridgea.h"
  14#include "audio_manager.h"
  15
  16/*
  17 * gb_snd management functions
  18 */
  19
  20static int gbaudio_request_jack(struct gbaudio_module_info *module,
  21                                struct gb_audio_jack_event_request *req)
  22{
  23        int report;
  24        struct snd_jack *jack = module->headset.jack.jack;
  25        struct snd_jack *btn_jack = module->button.jack.jack;
  26
  27        if (!jack) {
  28                dev_err_ratelimited(module->dev,
  29                                    "Invalid jack event received:type: %u, event: %u\n",
  30                                    req->jack_attribute, req->event);
  31                return -EINVAL;
  32        }
  33
  34        dev_warn_ratelimited(module->dev,
  35                             "Jack Event received: type: %u, event: %u\n",
  36                             req->jack_attribute, req->event);
  37
  38        if (req->event == GB_AUDIO_JACK_EVENT_REMOVAL) {
  39                module->jack_type = 0;
  40                if (btn_jack && module->button_status) {
  41                        snd_soc_jack_report(&module->button.jack, 0,
  42                                            module->button_mask);
  43                        module->button_status = 0;
  44                }
  45                snd_soc_jack_report(&module->headset.jack, 0,
  46                                    module->jack_mask);
  47                return 0;
  48        }
  49
  50        report = req->jack_attribute & module->jack_mask;
  51        if (!report) {
  52                dev_err_ratelimited(module->dev,
  53                                    "Invalid jack event received:type: %u, event: %u\n",
  54                                    req->jack_attribute, req->event);
  55                return -EINVAL;
  56        }
  57
  58        if (module->jack_type)
  59                dev_warn_ratelimited(module->dev,
  60                                     "Modifying jack from %d to %d\n",
  61                                     module->jack_type, report);
  62
  63        module->jack_type = report;
  64        snd_soc_jack_report(&module->headset.jack, report, module->jack_mask);
  65
  66        return 0;
  67}
  68
  69static int gbaudio_request_button(struct gbaudio_module_info *module,
  70                                  struct gb_audio_button_event_request *req)
  71{
  72        int soc_button_id, report;
  73        struct snd_jack *btn_jack = module->button.jack.jack;
  74
  75        if (!btn_jack) {
  76                dev_err_ratelimited(module->dev,
  77                                    "Invalid button event received:type: %u, event: %u\n",
  78                                    req->button_id, req->event);
  79                return -EINVAL;
  80        }
  81
  82        dev_warn_ratelimited(module->dev,
  83                             "Button Event received: id: %u, event: %u\n",
  84                             req->button_id, req->event);
  85
  86        /* currently supports 4 buttons only */
  87        if (!module->jack_type) {
  88                dev_err_ratelimited(module->dev,
  89                                    "Jack not present. Bogus event!!\n");
  90                return -EINVAL;
  91        }
  92
  93        report = module->button_status & module->button_mask;
  94        soc_button_id = 0;
  95
  96        switch (req->button_id) {
  97        case 1:
  98                soc_button_id = SND_JACK_BTN_0 & module->button_mask;
  99                break;
 100
 101        case 2:
 102                soc_button_id = SND_JACK_BTN_1 & module->button_mask;
 103                break;
 104
 105        case 3:
 106                soc_button_id = SND_JACK_BTN_2 & module->button_mask;
 107                break;
 108
 109        case 4:
 110                soc_button_id = SND_JACK_BTN_3 & module->button_mask;
 111                break;
 112        }
 113
 114        if (!soc_button_id) {
 115                dev_err_ratelimited(module->dev,
 116                                    "Invalid button request received\n");
 117                return -EINVAL;
 118        }
 119
 120        if (req->event == GB_AUDIO_BUTTON_EVENT_PRESS)
 121                report = report | soc_button_id;
 122        else
 123                report = report & ~soc_button_id;
 124
 125        module->button_status = report;
 126
 127        snd_soc_jack_report(&module->button.jack, report, module->button_mask);
 128
 129        return 0;
 130}
 131
 132static int gbaudio_request_stream(struct gbaudio_module_info *module,
 133                                  struct gb_audio_streaming_event_request *req)
 134{
 135        dev_warn(module->dev, "Audio Event received: cport: %u, event: %u\n",
 136                 le16_to_cpu(req->data_cport), req->event);
 137
 138        return 0;
 139}
 140
 141static int gbaudio_codec_request_handler(struct gb_operation *op)
 142{
 143        struct gb_connection *connection = op->connection;
 144        struct gbaudio_module_info *module =
 145                greybus_get_drvdata(connection->bundle);
 146        struct gb_operation_msg_hdr *header = op->request->header;
 147        struct gb_audio_streaming_event_request *stream_req;
 148        struct gb_audio_jack_event_request *jack_req;
 149        struct gb_audio_button_event_request *button_req;
 150        int ret;
 151
 152        switch (header->type) {
 153        case GB_AUDIO_TYPE_STREAMING_EVENT:
 154                stream_req = op->request->payload;
 155                ret = gbaudio_request_stream(module, stream_req);
 156                break;
 157
 158        case GB_AUDIO_TYPE_JACK_EVENT:
 159                jack_req = op->request->payload;
 160                ret = gbaudio_request_jack(module, jack_req);
 161                break;
 162
 163        case GB_AUDIO_TYPE_BUTTON_EVENT:
 164                button_req = op->request->payload;
 165                ret = gbaudio_request_button(module, button_req);
 166                break;
 167
 168        default:
 169                dev_err_ratelimited(&connection->bundle->dev,
 170                                    "Invalid Audio Event received\n");
 171                return -EINVAL;
 172        }
 173
 174        return ret;
 175}
 176
 177static int gb_audio_add_mgmt_connection(struct gbaudio_module_info *gbmodule,
 178                                        struct greybus_descriptor_cport *cport_desc,
 179                                        struct gb_bundle *bundle)
 180{
 181        struct gb_connection *connection;
 182
 183        /* Management Cport */
 184        if (gbmodule->mgmt_connection) {
 185                dev_err(&bundle->dev,
 186                        "Can't have multiple Management connections\n");
 187                return -ENODEV;
 188        }
 189
 190        connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),
 191                                          gbaudio_codec_request_handler);
 192        if (IS_ERR(connection))
 193                return PTR_ERR(connection);
 194
 195        greybus_set_drvdata(bundle, gbmodule);
 196        gbmodule->mgmt_connection = connection;
 197
 198        return 0;
 199}
 200
 201static int gb_audio_add_data_connection(struct gbaudio_module_info *gbmodule,
 202                                        struct greybus_descriptor_cport *cport_desc,
 203                                        struct gb_bundle *bundle)
 204{
 205        struct gb_connection *connection;
 206        struct gbaudio_data_connection *dai;
 207
 208        dai = devm_kzalloc(gbmodule->dev, sizeof(*dai), GFP_KERNEL);
 209        if (!dai)
 210                return -ENOMEM;
 211
 212        connection = gb_connection_create_offloaded(bundle,
 213                                                    le16_to_cpu(cport_desc->id),
 214                                                    GB_CONNECTION_FLAG_CSD);
 215        if (IS_ERR(connection)) {
 216                devm_kfree(gbmodule->dev, dai);
 217                return PTR_ERR(connection);
 218        }
 219
 220        greybus_set_drvdata(bundle, gbmodule);
 221        dai->id = 0;
 222        dai->data_cport = cpu_to_le16(connection->intf_cport_id);
 223        dai->connection = connection;
 224        list_add(&dai->list, &gbmodule->data_list);
 225
 226        return 0;
 227}
 228
 229/*
 230 * This is the basic hook get things initialized and registered w/ gb
 231 */
 232
 233static int gb_audio_probe(struct gb_bundle *bundle,
 234                          const struct greybus_bundle_id *id)
 235{
 236        struct device *dev = &bundle->dev;
 237        struct gbaudio_module_info *gbmodule;
 238        struct greybus_descriptor_cport *cport_desc;
 239        struct gb_audio_manager_module_descriptor desc;
 240        struct gbaudio_data_connection *dai, *_dai;
 241        int ret, i;
 242        struct gb_audio_topology *topology;
 243
 244        /* There should be at least one Management and one Data cport */
 245        if (bundle->num_cports < 2)
 246                return -ENODEV;
 247
 248        /*
 249         * There can be only one Management connection and any number of data
 250         * connections.
 251         */
 252        gbmodule = devm_kzalloc(dev, sizeof(*gbmodule), GFP_KERNEL);
 253        if (!gbmodule)
 254                return -ENOMEM;
 255
 256        gbmodule->num_data_connections = bundle->num_cports - 1;
 257        INIT_LIST_HEAD(&gbmodule->data_list);
 258        INIT_LIST_HEAD(&gbmodule->widget_list);
 259        INIT_LIST_HEAD(&gbmodule->ctl_list);
 260        INIT_LIST_HEAD(&gbmodule->widget_ctl_list);
 261        INIT_LIST_HEAD(&gbmodule->jack_list);
 262        gbmodule->dev = dev;
 263        snprintf(gbmodule->name, sizeof(gbmodule->name), "%s.%s", dev->driver->name,
 264                 dev_name(dev));
 265        greybus_set_drvdata(bundle, gbmodule);
 266
 267        /* Create all connections */
 268        for (i = 0; i < bundle->num_cports; i++) {
 269                cport_desc = &bundle->cport_desc[i];
 270
 271                switch (cport_desc->protocol_id) {
 272                case GREYBUS_PROTOCOL_AUDIO_MGMT:
 273                        ret = gb_audio_add_mgmt_connection(gbmodule, cport_desc,
 274                                                           bundle);
 275                        if (ret)
 276                                goto destroy_connections;
 277                        break;
 278                case GREYBUS_PROTOCOL_AUDIO_DATA:
 279                        ret = gb_audio_add_data_connection(gbmodule, cport_desc,
 280                                                           bundle);
 281                        if (ret)
 282                                goto destroy_connections;
 283                        break;
 284                default:
 285                        dev_err(dev, "Unsupported protocol: 0x%02x\n",
 286                                cport_desc->protocol_id);
 287                        ret = -ENODEV;
 288                        goto destroy_connections;
 289                }
 290        }
 291
 292        /* There must be a management cport */
 293        if (!gbmodule->mgmt_connection) {
 294                ret = -EINVAL;
 295                dev_err(dev, "Missing management connection\n");
 296                goto destroy_connections;
 297        }
 298
 299        /* Initialize management connection */
 300        ret = gb_connection_enable(gbmodule->mgmt_connection);
 301        if (ret) {
 302                dev_err(dev, "%d: Error while enabling mgmt connection\n", ret);
 303                goto destroy_connections;
 304        }
 305        gbmodule->dev_id = gbmodule->mgmt_connection->intf->interface_id;
 306
 307        /*
 308         * FIXME: malloc for topology happens via audio_gb driver
 309         * should be done within codec driver itself
 310         */
 311        ret = gb_audio_gb_get_topology(gbmodule->mgmt_connection, &topology);
 312        if (ret) {
 313                dev_err(dev, "%d:Error while fetching topology\n", ret);
 314                goto disable_connection;
 315        }
 316
 317        /* process topology data */
 318        ret = gbaudio_tplg_parse_data(gbmodule, topology);
 319        if (ret) {
 320                dev_err(dev, "%d:Error while parsing topology data\n",
 321                        ret);
 322                goto free_topology;
 323        }
 324        gbmodule->topology = topology;
 325
 326        /* Initialize data connections */
 327        list_for_each_entry(dai, &gbmodule->data_list, list) {
 328                ret = gb_connection_enable(dai->connection);
 329                if (ret) {
 330                        dev_err(dev,
 331                                "%d:Error while enabling %d:data connection\n",
 332                                ret, le16_to_cpu(dai->data_cport));
 333                        goto disable_data_connection;
 334                }
 335        }
 336
 337        /* register module with gbcodec */
 338        ret = gbaudio_register_module(gbmodule);
 339        if (ret)
 340                goto disable_data_connection;
 341
 342        /* inform above layer for uevent */
 343        dev_dbg(dev, "Inform set_event:%d to above layer\n", 1);
 344        /* prepare for the audio manager */
 345        strscpy(desc.name, gbmodule->name, sizeof(desc.name));
 346        desc.vid = 2; /* todo */
 347        desc.pid = 3; /* todo */
 348        desc.intf_id = gbmodule->dev_id;
 349        desc.op_devices = gbmodule->op_devices;
 350        desc.ip_devices = gbmodule->ip_devices;
 351        gbmodule->manager_id = gb_audio_manager_add(&desc);
 352
 353        dev_dbg(dev, "Add GB Audio device:%s\n", gbmodule->name);
 354
 355        gb_pm_runtime_put_autosuspend(bundle);
 356
 357        return 0;
 358
 359disable_data_connection:
 360        list_for_each_entry_safe(dai, _dai, &gbmodule->data_list, list)
 361                gb_connection_disable(dai->connection);
 362        gbaudio_tplg_release(gbmodule);
 363        gbmodule->topology = NULL;
 364
 365free_topology:
 366        kfree(topology);
 367
 368disable_connection:
 369        gb_connection_disable(gbmodule->mgmt_connection);
 370
 371destroy_connections:
 372        list_for_each_entry_safe(dai, _dai, &gbmodule->data_list, list) {
 373                gb_connection_destroy(dai->connection);
 374                list_del(&dai->list);
 375                devm_kfree(dev, dai);
 376        }
 377
 378        if (gbmodule->mgmt_connection)
 379                gb_connection_destroy(gbmodule->mgmt_connection);
 380
 381        devm_kfree(dev, gbmodule);
 382
 383        return ret;
 384}
 385
 386static void gb_audio_disconnect(struct gb_bundle *bundle)
 387{
 388        struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle);
 389        struct gbaudio_data_connection *dai, *_dai;
 390
 391        gb_pm_runtime_get_sync(bundle);
 392
 393        /* cleanup module related resources first */
 394        gbaudio_unregister_module(gbmodule);
 395
 396        /* inform uevent to above layers */
 397        gb_audio_manager_remove(gbmodule->manager_id);
 398
 399        gbaudio_tplg_release(gbmodule);
 400        kfree(gbmodule->topology);
 401        gbmodule->topology = NULL;
 402        gb_connection_disable(gbmodule->mgmt_connection);
 403        list_for_each_entry_safe(dai, _dai, &gbmodule->data_list, list) {
 404                gb_connection_disable(dai->connection);
 405                gb_connection_destroy(dai->connection);
 406                list_del(&dai->list);
 407                devm_kfree(gbmodule->dev, dai);
 408        }
 409        gb_connection_destroy(gbmodule->mgmt_connection);
 410        gbmodule->mgmt_connection = NULL;
 411
 412        devm_kfree(&bundle->dev, gbmodule);
 413}
 414
 415static const struct greybus_bundle_id gb_audio_id_table[] = {
 416        { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO) },
 417        { }
 418};
 419MODULE_DEVICE_TABLE(greybus, gb_audio_id_table);
 420
 421#ifdef CONFIG_PM
 422static int gb_audio_suspend(struct device *dev)
 423{
 424        struct gb_bundle *bundle = to_gb_bundle(dev);
 425        struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle);
 426        struct gbaudio_data_connection *dai;
 427
 428        list_for_each_entry(dai, &gbmodule->data_list, list)
 429                gb_connection_disable(dai->connection);
 430
 431        gb_connection_disable(gbmodule->mgmt_connection);
 432
 433        return 0;
 434}
 435
 436static int gb_audio_resume(struct device *dev)
 437{
 438        struct gb_bundle *bundle = to_gb_bundle(dev);
 439        struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle);
 440        struct gbaudio_data_connection *dai;
 441        int ret;
 442
 443        ret = gb_connection_enable(gbmodule->mgmt_connection);
 444        if (ret) {
 445                dev_err(dev, "%d:Error while enabling mgmt connection\n", ret);
 446                return ret;
 447        }
 448
 449        list_for_each_entry(dai, &gbmodule->data_list, list) {
 450                ret = gb_connection_enable(dai->connection);
 451                if (ret) {
 452                        dev_err(dev,
 453                                "%d:Error while enabling %d:data connection\n",
 454                                ret, le16_to_cpu(dai->data_cport));
 455                        return ret;
 456                }
 457        }
 458
 459        return 0;
 460}
 461#endif
 462
 463static const struct dev_pm_ops gb_audio_pm_ops = {
 464        SET_RUNTIME_PM_OPS(gb_audio_suspend, gb_audio_resume, NULL)
 465};
 466
 467static struct greybus_driver gb_audio_driver = {
 468        .name           = "gb-audio",
 469        .probe          = gb_audio_probe,
 470        .disconnect     = gb_audio_disconnect,
 471        .id_table       = gb_audio_id_table,
 472        .driver.pm      = &gb_audio_pm_ops,
 473};
 474module_greybus_driver(gb_audio_driver);
 475
 476MODULE_DESCRIPTION("Greybus Audio module driver");
 477MODULE_AUTHOR("Vaibhav Agarwal <vaibhav.agarwal@linaro.org>");
 478MODULE_LICENSE("GPL v2");
 479MODULE_ALIAS("platform:gbaudio-module");
 480