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                 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                dev_err(gbmodule->dev, "DAI Malloc failure\n");
 212                return -ENOMEM;
 213        }
 214
 215        connection = gb_connection_create_offloaded(bundle,
 216                                        le16_to_cpu(cport_desc->id),
 217                                        GB_CONNECTION_FLAG_CSD);
 218        if (IS_ERR(connection)) {
 219                devm_kfree(gbmodule->dev, dai);
 220                return PTR_ERR(connection);
 221        }
 222
 223        greybus_set_drvdata(bundle, gbmodule);
 224        dai->id = 0;
 225        dai->data_cport = connection->intf_cport_id;
 226        dai->connection = connection;
 227        list_add(&dai->list, &gbmodule->data_list);
 228
 229        return 0;
 230}
 231
 232/*
 233 * This is the basic hook get things initialized and registered w/ gb
 234 */
 235
 236static int gb_audio_probe(struct gb_bundle *bundle,
 237                          const struct greybus_bundle_id *id)
 238{
 239        struct device *dev = &bundle->dev;
 240        struct gbaudio_module_info *gbmodule;
 241        struct greybus_descriptor_cport *cport_desc;
 242        struct gb_audio_manager_module_descriptor desc;
 243        struct gbaudio_data_connection *dai, *_dai;
 244        int ret, i;
 245        struct gb_audio_topology *topology;
 246
 247        /* There should be at least one Management and one Data cport */
 248        if (bundle->num_cports < 2)
 249                return -ENODEV;
 250
 251        /*
 252         * There can be only one Management connection and any number of data
 253         * connections.
 254         */
 255        gbmodule = devm_kzalloc(dev, sizeof(*gbmodule), GFP_KERNEL);
 256        if (!gbmodule)
 257                return -ENOMEM;
 258
 259        gbmodule->num_data_connections = bundle->num_cports - 1;
 260        INIT_LIST_HEAD(&gbmodule->data_list);
 261        INIT_LIST_HEAD(&gbmodule->widget_list);
 262        INIT_LIST_HEAD(&gbmodule->ctl_list);
 263        INIT_LIST_HEAD(&gbmodule->widget_ctl_list);
 264        gbmodule->dev = dev;
 265        snprintf(gbmodule->name, NAME_SIZE, "%s.%s", dev->driver->name,
 266                 dev_name(dev));
 267        greybus_set_drvdata(bundle, gbmodule);
 268
 269        /* Create all connections */
 270        for (i = 0; i < bundle->num_cports; i++) {
 271                cport_desc = &bundle->cport_desc[i];
 272
 273                switch (cport_desc->protocol_id) {
 274                case GREYBUS_PROTOCOL_AUDIO_MGMT:
 275                        ret = gb_audio_add_mgmt_connection(gbmodule, cport_desc,
 276                                                           bundle);
 277                        if (ret)
 278                                goto destroy_connections;
 279                        break;
 280                case GREYBUS_PROTOCOL_AUDIO_DATA:
 281                        ret = gb_audio_add_data_connection(gbmodule, cport_desc,
 282                                                           bundle);
 283                        if (ret)
 284                                goto destroy_connections;
 285                        break;
 286                default:
 287                        dev_err(dev, "Unsupported protocol: 0x%02x\n",
 288                                cport_desc->protocol_id);
 289                        ret = -ENODEV;
 290                        goto destroy_connections;
 291                }
 292        }
 293
 294        /* There must be a management cport */
 295        if (!gbmodule->mgmt_connection) {
 296                ret = -EINVAL;
 297                dev_err(dev, "Missing management connection\n");
 298                goto destroy_connections;
 299        }
 300
 301        /* Initialize management connection */
 302        ret = gb_connection_enable(gbmodule->mgmt_connection);
 303        if (ret) {
 304                dev_err(dev, "%d: Error while enabling mgmt connection\n", ret);
 305                goto destroy_connections;
 306        }
 307        gbmodule->dev_id = gbmodule->mgmt_connection->intf->interface_id;
 308
 309        /*
 310         * FIXME: malloc for topology happens via audio_gb driver
 311         * should be done within codec driver itself
 312         */
 313        ret = gb_audio_gb_get_topology(gbmodule->mgmt_connection, &topology);
 314        if (ret) {
 315                dev_err(dev, "%d:Error while fetching topology\n", ret);
 316                goto disable_connection;
 317        }
 318
 319        /* process topology data */
 320        ret = gbaudio_tplg_parse_data(gbmodule, topology);
 321        if (ret) {
 322                dev_err(dev, "%d:Error while parsing topology data\n",
 323                          ret);
 324                goto free_topology;
 325        }
 326        gbmodule->topology = topology;
 327
 328        /* Initialize data connections */
 329        list_for_each_entry(dai, &gbmodule->data_list, list) {
 330                ret = gb_connection_enable(dai->connection);
 331                if (ret) {
 332                        dev_err(dev,
 333                                "%d:Error while enabling %d:data connection\n",
 334                                ret, dai->data_cport);
 335                        goto disable_data_connection;
 336                }
 337        }
 338
 339        /* register module with gbcodec */
 340        ret = gbaudio_register_module(gbmodule);
 341        if (ret)
 342                goto disable_data_connection;
 343
 344        /* inform above layer for uevent */
 345        dev_dbg(dev, "Inform set_event:%d to above layer\n", 1);
 346        /* prepare for the audio manager */
 347        strlcpy(desc.name, gbmodule->name, GB_AUDIO_MANAGER_MODULE_NAME_LEN);
 348        desc.slot = 1; /* todo */
 349        desc.vid = 2; /* todo */
 350        desc.pid = 3; /* todo */
 351        desc.cport = gbmodule->dev_id;
 352        desc.op_devices = gbmodule->op_devices;
 353        desc.ip_devices = gbmodule->ip_devices;
 354        gbmodule->manager_id = gb_audio_manager_add(&desc);
 355
 356        dev_dbg(dev, "Add GB Audio device:%s\n", gbmodule->name);
 357
 358        gb_pm_runtime_put_autosuspend(bundle);
 359
 360        return 0;
 361
 362disable_data_connection:
 363        list_for_each_entry_safe(dai, _dai, &gbmodule->data_list, list)
 364                gb_connection_disable(dai->connection);
 365        gbaudio_tplg_release(gbmodule);
 366        gbmodule->topology = NULL;
 367
 368free_topology:
 369        kfree(topology);
 370
 371disable_connection:
 372        gb_connection_disable(gbmodule->mgmt_connection);
 373
 374destroy_connections:
 375        list_for_each_entry_safe(dai, _dai, &gbmodule->data_list, list) {
 376                gb_connection_destroy(dai->connection);
 377                list_del(&dai->list);
 378                devm_kfree(dev, dai);
 379        }
 380
 381        if (gbmodule->mgmt_connection)
 382                gb_connection_destroy(gbmodule->mgmt_connection);
 383
 384        devm_kfree(dev, gbmodule);
 385
 386        return ret;
 387}
 388
 389static void gb_audio_disconnect(struct gb_bundle *bundle)
 390{
 391        struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle);
 392        struct gbaudio_data_connection *dai, *_dai;
 393
 394        gb_pm_runtime_get_sync(bundle);
 395
 396        /* cleanup module related resources first */
 397        gbaudio_unregister_module(gbmodule);
 398
 399        /* inform uevent to above layers */
 400        gb_audio_manager_remove(gbmodule->manager_id);
 401
 402        gbaudio_tplg_release(gbmodule);
 403        kfree(gbmodule->topology);
 404        gbmodule->topology = NULL;
 405        gb_connection_disable(gbmodule->mgmt_connection);
 406        list_for_each_entry_safe(dai, _dai, &gbmodule->data_list, list) {
 407                gb_connection_disable(dai->connection);
 408                gb_connection_destroy(dai->connection);
 409                list_del(&dai->list);
 410                devm_kfree(gbmodule->dev, dai);
 411        }
 412        gb_connection_destroy(gbmodule->mgmt_connection);
 413        gbmodule->mgmt_connection = NULL;
 414
 415        devm_kfree(&bundle->dev, gbmodule);
 416}
 417
 418static const struct greybus_bundle_id gb_audio_id_table[] = {
 419        { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO) },
 420        { }
 421};
 422MODULE_DEVICE_TABLE(greybus, gb_audio_id_table);
 423
 424#ifdef CONFIG_PM
 425static int gb_audio_suspend(struct device *dev)
 426{
 427        struct gb_bundle *bundle = to_gb_bundle(dev);
 428        struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle);
 429        struct gbaudio_data_connection *dai;
 430
 431        list_for_each_entry(dai, &gbmodule->data_list, list)
 432                gb_connection_disable(dai->connection);
 433
 434        gb_connection_disable(gbmodule->mgmt_connection);
 435
 436        return 0;
 437}
 438
 439static int gb_audio_resume(struct device *dev)
 440{
 441        struct gb_bundle *bundle = to_gb_bundle(dev);
 442        struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle);
 443        struct gbaudio_data_connection *dai;
 444        int ret;
 445
 446        ret = gb_connection_enable(gbmodule->mgmt_connection);
 447        if (ret) {
 448                dev_err(dev, "%d:Error while enabling mgmt connection\n", ret);
 449                return ret;
 450        }
 451
 452        list_for_each_entry(dai, &gbmodule->data_list, list) {
 453                ret = gb_connection_enable(dai->connection);
 454                if (ret) {
 455                        dev_err(dev,
 456                                "%d:Error while enabling %d:data connection\n",
 457                                ret, dai->data_cport);
 458                        return ret;
 459                }
 460        }
 461
 462        return 0;
 463}
 464#endif
 465
 466static const struct dev_pm_ops gb_audio_pm_ops = {
 467        SET_RUNTIME_PM_OPS(gb_audio_suspend, gb_audio_resume, NULL)
 468};
 469
 470static struct greybus_driver gb_audio_driver = {
 471        .name           = "gb-audio",
 472        .probe          = gb_audio_probe,
 473        .disconnect     = gb_audio_disconnect,
 474        .id_table       = gb_audio_id_table,
 475        .driver.pm      = &gb_audio_pm_ops,
 476};
 477module_greybus_driver(gb_audio_driver);
 478
 479MODULE_DESCRIPTION("Greybus Audio module driver");
 480MODULE_AUTHOR("Vaibhav Agarwal <vaibhav.agarwal@linaro.org>");
 481MODULE_LICENSE("GPL v2");
 482MODULE_ALIAS("platform:gbaudio-module");
 483