linux/drivers/staging/greybus/audio_module.c
<<
>>
Prefs
   1/*
   2 * Greybus audio driver
   3 * Copyright 2015 Google Inc.
   4 * Copyright 2015 Linaro Ltd.
   5 *
   6 * Released under the GPLv2 only.
   7 */
   8#include <linux/kernel.h>
   9#include <linux/module.h>
  10#include <sound/soc.h>
  11#include <sound/pcm_params.h>
  12
  13#include "audio_codec.h"
  14#include "audio_apbridgea.h"
  15#include "audio_manager.h"
  16
  17/*
  18 * gb_snd management functions
  19 */
  20
  21static int gbaudio_request_jack(struct gbaudio_module_info *module,
  22                                  struct gb_audio_jack_event_request *req)
  23{
  24        int report;
  25        struct snd_jack *jack = module->headset_jack.jack;
  26        struct snd_jack *btn_jack = module->button_jack.jack;
  27
  28        if (!jack) {
  29                dev_err_ratelimited(module->dev,
  30                        "Invalid jack event received:type: %u, event: %u\n",
  31                        req->jack_attribute, req->event);
  32                return -EINVAL;
  33        }
  34
  35        dev_warn_ratelimited(module->dev,
  36                             "Jack Event received: type: %u, event: %u\n",
  37                             req->jack_attribute, req->event);
  38
  39        if (req->event == GB_AUDIO_JACK_EVENT_REMOVAL) {
  40                module->jack_type = 0;
  41                if (btn_jack && module->button_status) {
  42                        snd_soc_jack_report(&module->button_jack, 0,
  43                                            module->button_mask);
  44                        module->button_status = 0;
  45                }
  46                snd_soc_jack_report(&module->headset_jack, 0,
  47                                    module->jack_mask);
  48                return 0;
  49        }
  50
  51        report = req->jack_attribute & module->jack_mask;
  52        if (!report) {
  53                dev_err_ratelimited(module->dev,
  54                        "Invalid jack event received:type: %u, event: %u\n",
  55                        req->jack_attribute, req->event);
  56                return -EINVAL;
  57        }
  58
  59        if (module->jack_type)
  60                dev_warn_ratelimited(module->dev,
  61                                     "Modifying jack from %d to %d\n",
  62                                     module->jack_type, report);
  63
  64        module->jack_type = report;
  65        snd_soc_jack_report(&module->headset_jack, report, module->jack_mask);
  66
  67        return 0;
  68}
  69
  70static int gbaudio_request_button(struct gbaudio_module_info *module,
  71                                  struct gb_audio_button_event_request *req)
  72{
  73        int soc_button_id, report;
  74        struct snd_jack *btn_jack = module->button_jack.jack;
  75
  76        if (!btn_jack) {
  77                dev_err_ratelimited(module->dev,
  78                        "Invalid button event received:type: %u, event: %u\n",
  79                        req->button_id, req->event);
  80                return -EINVAL;
  81        }
  82
  83        dev_warn_ratelimited(module->dev,
  84                             "Button Event received: id: %u, event: %u\n",
  85                             req->button_id, req->event);
  86
  87        /* currently supports 4 buttons only */
  88        if (!module->jack_type) {
  89                dev_err_ratelimited(module->dev,
  90                                    "Jack not present. Bogus event!!\n");
  91                return -EINVAL;
  92        }
  93
  94        report = module->button_status & module->button_mask;
  95        soc_button_id = 0;
  96
  97        switch (req->button_id) {
  98        case 1:
  99                soc_button_id = SND_JACK_BTN_0 & module->button_mask;
 100                break;
 101
 102        case 2:
 103                soc_button_id = SND_JACK_BTN_1 & module->button_mask;
 104                break;
 105
 106        case 3:
 107                soc_button_id = SND_JACK_BTN_2 & module->button_mask;
 108                break;
 109
 110        case 4:
 111                soc_button_id = SND_JACK_BTN_3 & module->button_mask;
 112                break;
 113        }
 114
 115        if (!soc_button_id) {
 116                dev_err_ratelimited(module->dev,
 117                                    "Invalid button request received\n");
 118                return -EINVAL;
 119        }
 120
 121        if (req->event == GB_AUDIO_BUTTON_EVENT_PRESS)
 122                report = report | soc_button_id;
 123        else
 124                report = report & ~soc_button_id;
 125
 126        module->button_status = report;
 127
 128        snd_soc_jack_report(&module->button_jack, report, module->button_mask);
 129
 130        return 0;
 131}
 132
 133static int gbaudio_request_stream(struct gbaudio_module_info *module,
 134                                  struct gb_audio_streaming_event_request *req)
 135{
 136        dev_warn(module->dev, "Audio Event received: cport: %u, event: %u\n",
 137                 le16_to_cpu(req->data_cport), req->event);
 138
 139        return 0;
 140}
 141
 142static int gbaudio_codec_request_handler(struct gb_operation *op)
 143{
 144        struct gb_connection *connection = op->connection;
 145        struct gbaudio_module_info *module =
 146                greybus_get_drvdata(connection->bundle);
 147        struct gb_operation_msg_hdr *header = op->request->header;
 148        struct gb_audio_streaming_event_request *stream_req;
 149        struct gb_audio_jack_event_request *jack_req;
 150        struct gb_audio_button_event_request *button_req;
 151        int ret;
 152
 153        switch (header->type) {
 154        case GB_AUDIO_TYPE_STREAMING_EVENT:
 155                stream_req = op->request->payload;
 156                ret = gbaudio_request_stream(module, stream_req);
 157                break;
 158
 159        case GB_AUDIO_TYPE_JACK_EVENT:
 160                jack_req = op->request->payload;
 161                ret = gbaudio_request_jack(module, jack_req);
 162                break;
 163
 164        case GB_AUDIO_TYPE_BUTTON_EVENT:
 165                button_req = op->request->payload;
 166                ret = gbaudio_request_button(module, button_req);
 167                break;
 168
 169        default:
 170                dev_err_ratelimited(&connection->bundle->dev,
 171                                    "Invalid Audio Event received\n");
 172                return -EINVAL;
 173        }
 174
 175        return ret;
 176}
 177
 178static int gb_audio_add_mgmt_connection(struct gbaudio_module_info *gbmodule,
 179                                struct greybus_descriptor_cport *cport_desc,
 180                                struct gb_bundle *bundle)
 181{
 182        struct gb_connection *connection;
 183
 184        /* Management Cport */
 185        if (gbmodule->mgmt_connection) {
 186                dev_err(&bundle->dev,
 187                        "Can't have multiple Management connections\n");
 188                return -ENODEV;
 189        }
 190
 191        connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),
 192                                          gbaudio_codec_request_handler);
 193        if (IS_ERR(connection))
 194                return PTR_ERR(connection);
 195
 196        greybus_set_drvdata(bundle, gbmodule);
 197        gbmodule->mgmt_connection = connection;
 198
 199        return 0;
 200}
 201
 202static int gb_audio_add_data_connection(struct gbaudio_module_info *gbmodule,
 203                                struct greybus_descriptor_cport *cport_desc,
 204                                struct gb_bundle *bundle)
 205{
 206        struct gb_connection *connection;
 207        struct gbaudio_data_connection *dai;
 208
 209        dai = devm_kzalloc(gbmodule->dev, sizeof(*dai), GFP_KERNEL);
 210        if (!dai)
 211                return -ENOMEM;
 212
 213        connection = gb_connection_create_offloaded(bundle,
 214                                        le16_to_cpu(cport_desc->id),
 215                                        GB_CONNECTION_FLAG_CSD);
 216        if (IS_ERR(connection)) {
 217                devm_kfree(gbmodule->dev, dai);
 218                return PTR_ERR(connection);
 219        }
 220
 221        greybus_set_drvdata(bundle, gbmodule);
 222        dai->id = 0;
 223        dai->data_cport = connection->intf_cport_id;
 224        dai->connection = connection;
 225        list_add(&dai->list, &gbmodule->data_list);
 226
 227        return 0;
 228}
 229
 230/*
 231 * This is the basic hook get things initialized and registered w/ gb
 232 */
 233
 234static int gb_audio_probe(struct gb_bundle *bundle,
 235                          const struct greybus_bundle_id *id)
 236{
 237        struct device *dev = &bundle->dev;
 238        struct gbaudio_module_info *gbmodule;
 239        struct greybus_descriptor_cport *cport_desc;
 240        struct gb_audio_manager_module_descriptor desc;
 241        struct gbaudio_data_connection *dai, *_dai;
 242        int ret, i;
 243        struct gb_audio_topology *topology;
 244
 245        /* There should be at least one Management and one Data cport */
 246        if (bundle->num_cports < 2)
 247                return -ENODEV;
 248
 249        /*
 250         * There can be only one Management connection and any number of data
 251         * connections.
 252         */
 253        gbmodule = devm_kzalloc(dev, sizeof(*gbmodule), GFP_KERNEL);
 254        if (!gbmodule)
 255                return -ENOMEM;
 256
 257        gbmodule->num_data_connections = bundle->num_cports - 1;
 258        INIT_LIST_HEAD(&gbmodule->data_list);
 259        INIT_LIST_HEAD(&gbmodule->widget_list);
 260        INIT_LIST_HEAD(&gbmodule->ctl_list);
 261        INIT_LIST_HEAD(&gbmodule->widget_ctl_list);
 262        gbmodule->dev = dev;
 263        snprintf(gbmodule->name, NAME_SIZE, "%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, 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        strlcpy(desc.name, gbmodule->name, GB_AUDIO_MANAGER_MODULE_NAME_LEN);
 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, 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