linux/sound/soc/codecs/cs35l32.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * cs35l32.c -- CS35L32 ALSA SoC audio driver
   4 *
   5 * Copyright 2014 CirrusLogic, Inc.
   6 *
   7 * Author: Brian Austin <brian.austin@cirrus.com>
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/moduleparam.h>
  12#include <linux/kernel.h>
  13#include <linux/init.h>
  14#include <linux/delay.h>
  15#include <linux/i2c.h>
  16#include <linux/gpio.h>
  17#include <linux/regmap.h>
  18#include <linux/slab.h>
  19#include <linux/platform_device.h>
  20#include <linux/regulator/consumer.h>
  21#include <linux/gpio/consumer.h>
  22#include <linux/of_device.h>
  23#include <sound/core.h>
  24#include <sound/pcm.h>
  25#include <sound/pcm_params.h>
  26#include <sound/soc.h>
  27#include <sound/soc-dapm.h>
  28#include <sound/initval.h>
  29#include <sound/tlv.h>
  30#include <dt-bindings/sound/cs35l32.h>
  31
  32#include "cs35l32.h"
  33#include "cirrus_legacy.h"
  34
  35#define CS35L32_NUM_SUPPLIES 2
  36static const char *const cs35l32_supply_names[CS35L32_NUM_SUPPLIES] = {
  37        "VA",
  38        "VP",
  39};
  40
  41struct  cs35l32_private {
  42        struct regmap *regmap;
  43        struct snd_soc_component *component;
  44        struct regulator_bulk_data supplies[CS35L32_NUM_SUPPLIES];
  45        struct cs35l32_platform_data pdata;
  46        struct gpio_desc *reset_gpio;
  47};
  48
  49static const struct reg_default cs35l32_reg_defaults[] = {
  50
  51        { 0x06, 0x04 }, /* Power Ctl 1 */
  52        { 0x07, 0xE8 }, /* Power Ctl 2 */
  53        { 0x08, 0x40 }, /* Clock Ctl */
  54        { 0x09, 0x20 }, /* Low Battery Threshold */
  55        { 0x0A, 0x00 }, /* Voltage Monitor [RO] */
  56        { 0x0B, 0x40 }, /* Conv Peak Curr Protection CTL */
  57        { 0x0C, 0x07 }, /* IMON Scaling */
  58        { 0x0D, 0x03 }, /* Audio/LED Pwr Manager */
  59        { 0x0F, 0x20 }, /* Serial Port Control */
  60        { 0x10, 0x14 }, /* Class D Amp CTL */
  61        { 0x11, 0x00 }, /* Protection Release CTL */
  62        { 0x12, 0xFF }, /* Interrupt Mask 1 */
  63        { 0x13, 0xFF }, /* Interrupt Mask 2 */
  64        { 0x14, 0xFF }, /* Interrupt Mask 3 */
  65        { 0x19, 0x00 }, /* LED Flash Mode Current */
  66        { 0x1A, 0x00 }, /* LED Movie Mode Current */
  67        { 0x1B, 0x20 }, /* LED Flash Timer */
  68        { 0x1C, 0x00 }, /* LED Flash Inhibit Current */
  69};
  70
  71static bool cs35l32_readable_register(struct device *dev, unsigned int reg)
  72{
  73        switch (reg) {
  74        case CS35L32_DEVID_AB ... CS35L32_AUDIO_LED_MNGR:
  75        case CS35L32_ADSP_CTL ... CS35L32_FLASH_INHIBIT:
  76                return true;
  77        default:
  78                return false;
  79        }
  80}
  81
  82static bool cs35l32_volatile_register(struct device *dev, unsigned int reg)
  83{
  84        switch (reg) {
  85        case CS35L32_DEVID_AB ... CS35L32_REV_ID:
  86        case CS35L32_INT_STATUS_1 ... CS35L32_LED_STATUS:
  87                return true;
  88        default:
  89                return false;
  90        }
  91}
  92
  93static bool cs35l32_precious_register(struct device *dev, unsigned int reg)
  94{
  95        switch (reg) {
  96        case CS35L32_INT_STATUS_1 ... CS35L32_LED_STATUS:
  97                return true;
  98        default:
  99                return false;
 100        }
 101}
 102
 103static DECLARE_TLV_DB_SCALE(classd_ctl_tlv, 900, 300, 0);
 104
 105static const struct snd_kcontrol_new imon_ctl =
 106        SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 6, 1, 1);
 107
 108static const struct snd_kcontrol_new vmon_ctl =
 109        SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 7, 1, 1);
 110
 111static const struct snd_kcontrol_new vpmon_ctl =
 112        SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 5, 1, 1);
 113
 114static const struct snd_kcontrol_new cs35l32_snd_controls[] = {
 115        SOC_SINGLE_TLV("Speaker Volume", CS35L32_CLASSD_CTL,
 116                       3, 0x04, 1, classd_ctl_tlv),
 117        SOC_SINGLE("Zero Cross Switch", CS35L32_CLASSD_CTL, 2, 1, 0),
 118        SOC_SINGLE("Gain Manager Switch", CS35L32_AUDIO_LED_MNGR, 3, 1, 0),
 119};
 120
 121static const struct snd_soc_dapm_widget cs35l32_dapm_widgets[] = {
 122
 123        SND_SOC_DAPM_SUPPLY("BOOST", CS35L32_PWRCTL1, 2, 1, NULL, 0),
 124        SND_SOC_DAPM_OUT_DRV("Speaker", CS35L32_PWRCTL1, 7, 1, NULL, 0),
 125
 126        SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L32_PWRCTL2, 3, 1),
 127
 128        SND_SOC_DAPM_INPUT("VP"),
 129        SND_SOC_DAPM_INPUT("ISENSE"),
 130        SND_SOC_DAPM_INPUT("VSENSE"),
 131
 132        SND_SOC_DAPM_SWITCH("VMON ADC", CS35L32_PWRCTL2, 7, 1, &vmon_ctl),
 133        SND_SOC_DAPM_SWITCH("IMON ADC", CS35L32_PWRCTL2, 6, 1, &imon_ctl),
 134        SND_SOC_DAPM_SWITCH("VPMON ADC", CS35L32_PWRCTL2, 5, 1, &vpmon_ctl),
 135};
 136
 137static const struct snd_soc_dapm_route cs35l32_audio_map[] = {
 138
 139        {"Speaker", NULL, "BOOST"},
 140
 141        {"VMON ADC", NULL, "VSENSE"},
 142        {"IMON ADC", NULL, "ISENSE"},
 143        {"VPMON ADC", NULL, "VP"},
 144
 145        {"SDOUT", "Switch", "VMON ADC"},
 146        {"SDOUT",  "Switch", "IMON ADC"},
 147        {"SDOUT", "Switch", "VPMON ADC"},
 148
 149        {"Capture", NULL, "SDOUT"},
 150};
 151
 152static int cs35l32_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
 153{
 154        struct snd_soc_component *component = codec_dai->component;
 155
 156        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 157        case SND_SOC_DAIFMT_CBM_CFM:
 158                snd_soc_component_update_bits(component, CS35L32_ADSP_CTL,
 159                                    CS35L32_ADSP_MASTER_MASK,
 160                                CS35L32_ADSP_MASTER_MASK);
 161                break;
 162        case SND_SOC_DAIFMT_CBS_CFS:
 163                snd_soc_component_update_bits(component, CS35L32_ADSP_CTL,
 164                                    CS35L32_ADSP_MASTER_MASK, 0);
 165                break;
 166        default:
 167                return -EINVAL;
 168        }
 169
 170        return 0;
 171}
 172
 173static int cs35l32_set_tristate(struct snd_soc_dai *dai, int tristate)
 174{
 175        struct snd_soc_component *component = dai->component;
 176
 177        return snd_soc_component_update_bits(component, CS35L32_PWRCTL2,
 178                                        CS35L32_SDOUT_3ST, tristate << 3);
 179}
 180
 181static const struct snd_soc_dai_ops cs35l32_ops = {
 182        .set_fmt = cs35l32_set_dai_fmt,
 183        .set_tristate = cs35l32_set_tristate,
 184};
 185
 186static struct snd_soc_dai_driver cs35l32_dai[] = {
 187        {
 188                .name = "cs35l32-monitor",
 189                .id = 0,
 190                .capture = {
 191                        .stream_name = "Capture",
 192                        .channels_min = 2,
 193                        .channels_max = 2,
 194                        .rates = CS35L32_RATES,
 195                        .formats = CS35L32_FORMATS,
 196                },
 197                .ops = &cs35l32_ops,
 198                .symmetric_rate = 1,
 199        }
 200};
 201
 202static int cs35l32_component_set_sysclk(struct snd_soc_component *component,
 203                              int clk_id, int source, unsigned int freq, int dir)
 204{
 205        unsigned int val;
 206
 207        switch (freq) {
 208        case 6000000:
 209                val = CS35L32_MCLK_RATIO;
 210                break;
 211        case 12000000:
 212                val = CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO;
 213                break;
 214        case 6144000:
 215                val = 0;
 216                break;
 217        case 12288000:
 218                val = CS35L32_MCLK_DIV2_MASK;
 219                break;
 220        default:
 221                return -EINVAL;
 222        }
 223
 224        return snd_soc_component_update_bits(component, CS35L32_CLK_CTL,
 225                        CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO_MASK, val);
 226}
 227
 228static const struct snd_soc_component_driver soc_component_dev_cs35l32 = {
 229        .set_sysclk             = cs35l32_component_set_sysclk,
 230        .controls               = cs35l32_snd_controls,
 231        .num_controls           = ARRAY_SIZE(cs35l32_snd_controls),
 232        .dapm_widgets           = cs35l32_dapm_widgets,
 233        .num_dapm_widgets       = ARRAY_SIZE(cs35l32_dapm_widgets),
 234        .dapm_routes            = cs35l32_audio_map,
 235        .num_dapm_routes        = ARRAY_SIZE(cs35l32_audio_map),
 236        .idle_bias_on           = 1,
 237        .use_pmdown_time        = 1,
 238        .endianness             = 1,
 239        .non_legacy_dai_naming  = 1,
 240};
 241
 242/* Current and threshold powerup sequence Pg37 in datasheet */
 243static const struct reg_sequence cs35l32_monitor_patch[] = {
 244
 245        { 0x00, 0x99 },
 246        { 0x48, 0x17 },
 247        { 0x49, 0x56 },
 248        { 0x43, 0x01 },
 249        { 0x3B, 0x62 },
 250        { 0x3C, 0x80 },
 251        { 0x00, 0x00 },
 252};
 253
 254static const struct regmap_config cs35l32_regmap = {
 255        .reg_bits = 8,
 256        .val_bits = 8,
 257
 258        .max_register = CS35L32_MAX_REGISTER,
 259        .reg_defaults = cs35l32_reg_defaults,
 260        .num_reg_defaults = ARRAY_SIZE(cs35l32_reg_defaults),
 261        .volatile_reg = cs35l32_volatile_register,
 262        .readable_reg = cs35l32_readable_register,
 263        .precious_reg = cs35l32_precious_register,
 264        .cache_type = REGCACHE_RBTREE,
 265
 266        .use_single_read = true,
 267        .use_single_write = true,
 268};
 269
 270static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
 271                                    struct cs35l32_platform_data *pdata)
 272{
 273        struct device_node *np = i2c_client->dev.of_node;
 274        unsigned int val;
 275
 276        if (of_property_read_u32(np, "cirrus,sdout-share", &val) >= 0)
 277                pdata->sdout_share = val;
 278
 279        if (of_property_read_u32(np, "cirrus,boost-manager", &val))
 280                val = -1u;
 281
 282        switch (val) {
 283        case CS35L32_BOOST_MGR_AUTO:
 284        case CS35L32_BOOST_MGR_AUTO_AUDIO:
 285        case CS35L32_BOOST_MGR_BYPASS:
 286        case CS35L32_BOOST_MGR_FIXED:
 287                pdata->boost_mng = val;
 288                break;
 289        case -1u:
 290        default:
 291                dev_err(&i2c_client->dev,
 292                        "Wrong cirrus,boost-manager DT value %d\n", val);
 293                pdata->boost_mng = CS35L32_BOOST_MGR_BYPASS;
 294        }
 295
 296        if (of_property_read_u32(np, "cirrus,sdout-datacfg", &val))
 297                val = -1u;
 298        switch (val) {
 299        case CS35L32_DATA_CFG_LR_VP:
 300        case CS35L32_DATA_CFG_LR_STAT:
 301        case CS35L32_DATA_CFG_LR:
 302        case CS35L32_DATA_CFG_LR_VPSTAT:
 303                pdata->sdout_datacfg = val;
 304                break;
 305        case -1u:
 306        default:
 307                dev_err(&i2c_client->dev,
 308                        "Wrong cirrus,sdout-datacfg DT value %d\n", val);
 309                pdata->sdout_datacfg = CS35L32_DATA_CFG_LR;
 310        }
 311
 312        if (of_property_read_u32(np, "cirrus,battery-threshold", &val))
 313                val = -1u;
 314        switch (val) {
 315        case CS35L32_BATT_THRESH_3_1V:
 316        case CS35L32_BATT_THRESH_3_2V:
 317        case CS35L32_BATT_THRESH_3_3V:
 318        case CS35L32_BATT_THRESH_3_4V:
 319                pdata->batt_thresh = val;
 320                break;
 321        case -1u:
 322        default:
 323                dev_err(&i2c_client->dev,
 324                        "Wrong cirrus,battery-threshold DT value %d\n", val);
 325                pdata->batt_thresh = CS35L32_BATT_THRESH_3_3V;
 326        }
 327
 328        if (of_property_read_u32(np, "cirrus,battery-recovery", &val))
 329                val = -1u;
 330        switch (val) {
 331        case CS35L32_BATT_RECOV_3_1V:
 332        case CS35L32_BATT_RECOV_3_2V:
 333        case CS35L32_BATT_RECOV_3_3V:
 334        case CS35L32_BATT_RECOV_3_4V:
 335        case CS35L32_BATT_RECOV_3_5V:
 336        case CS35L32_BATT_RECOV_3_6V:
 337                pdata->batt_recov = val;
 338                break;
 339        case -1u:
 340        default:
 341                dev_err(&i2c_client->dev,
 342                        "Wrong cirrus,battery-recovery DT value %d\n", val);
 343                pdata->batt_recov = CS35L32_BATT_RECOV_3_4V;
 344        }
 345
 346        return 0;
 347}
 348
 349static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
 350                                       const struct i2c_device_id *id)
 351{
 352        struct cs35l32_private *cs35l32;
 353        struct cs35l32_platform_data *pdata =
 354                dev_get_platdata(&i2c_client->dev);
 355        int ret, i, devid;
 356        unsigned int reg;
 357
 358        cs35l32 = devm_kzalloc(&i2c_client->dev, sizeof(*cs35l32), GFP_KERNEL);
 359        if (!cs35l32)
 360                return -ENOMEM;
 361
 362        i2c_set_clientdata(i2c_client, cs35l32);
 363
 364        cs35l32->regmap = devm_regmap_init_i2c(i2c_client, &cs35l32_regmap);
 365        if (IS_ERR(cs35l32->regmap)) {
 366                ret = PTR_ERR(cs35l32->regmap);
 367                dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
 368                return ret;
 369        }
 370
 371        if (pdata) {
 372                cs35l32->pdata = *pdata;
 373        } else {
 374                pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata),
 375                                     GFP_KERNEL);
 376                if (!pdata)
 377                        return -ENOMEM;
 378
 379                if (i2c_client->dev.of_node) {
 380                        ret = cs35l32_handle_of_data(i2c_client,
 381                                                     &cs35l32->pdata);
 382                        if (ret != 0)
 383                                return ret;
 384                }
 385        }
 386
 387        for (i = 0; i < ARRAY_SIZE(cs35l32->supplies); i++)
 388                cs35l32->supplies[i].supply = cs35l32_supply_names[i];
 389
 390        ret = devm_regulator_bulk_get(&i2c_client->dev,
 391                                      ARRAY_SIZE(cs35l32->supplies),
 392                                      cs35l32->supplies);
 393        if (ret != 0) {
 394                dev_err(&i2c_client->dev,
 395                        "Failed to request supplies: %d\n", ret);
 396                return ret;
 397        }
 398
 399        ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies),
 400                                    cs35l32->supplies);
 401        if (ret != 0) {
 402                dev_err(&i2c_client->dev,
 403                        "Failed to enable supplies: %d\n", ret);
 404                return ret;
 405        }
 406
 407        /* Reset the Device */
 408        cs35l32->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
 409                "reset", GPIOD_OUT_LOW);
 410        if (IS_ERR(cs35l32->reset_gpio)) {
 411                ret = PTR_ERR(cs35l32->reset_gpio);
 412                goto err_supplies;
 413        }
 414
 415        gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
 416
 417        /* initialize codec */
 418        devid = cirrus_read_device_id(cs35l32->regmap, CS35L32_DEVID_AB);
 419        if (devid < 0) {
 420                ret = devid;
 421                dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
 422                goto err_disable;
 423        }
 424
 425        if (devid != CS35L32_CHIP_ID) {
 426                ret = -ENODEV;
 427                dev_err(&i2c_client->dev,
 428                        "CS35L32 Device ID (%X). Expected %X\n",
 429                        devid, CS35L32_CHIP_ID);
 430                goto err_disable;
 431        }
 432
 433        ret = regmap_read(cs35l32->regmap, CS35L32_REV_ID, &reg);
 434        if (ret < 0) {
 435                dev_err(&i2c_client->dev, "Get Revision ID failed\n");
 436                goto err_disable;
 437        }
 438
 439        ret = regmap_register_patch(cs35l32->regmap, cs35l32_monitor_patch,
 440                                    ARRAY_SIZE(cs35l32_monitor_patch));
 441        if (ret < 0) {
 442                dev_err(&i2c_client->dev, "Failed to apply errata patch\n");
 443                goto err_disable;
 444        }
 445
 446        dev_info(&i2c_client->dev,
 447                 "Cirrus Logic CS35L32, Revision: %02X\n", reg & 0xFF);
 448
 449        /* Setup VBOOST Management */
 450        if (cs35l32->pdata.boost_mng)
 451                regmap_update_bits(cs35l32->regmap, CS35L32_AUDIO_LED_MNGR,
 452                                   CS35L32_BOOST_MASK,
 453                                cs35l32->pdata.boost_mng);
 454
 455        /* Setup ADSP Format Config */
 456        if (cs35l32->pdata.sdout_share)
 457                regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL,
 458                                    CS35L32_ADSP_SHARE_MASK,
 459                                cs35l32->pdata.sdout_share << 3);
 460
 461        /* Setup ADSP Data Configuration */
 462        if (cs35l32->pdata.sdout_datacfg)
 463                regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL,
 464                                   CS35L32_ADSP_DATACFG_MASK,
 465                                cs35l32->pdata.sdout_datacfg << 4);
 466
 467        /* Setup Low Battery Recovery  */
 468        if (cs35l32->pdata.batt_recov)
 469                regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD,
 470                                   CS35L32_BATT_REC_MASK,
 471                                cs35l32->pdata.batt_recov << 1);
 472
 473        /* Setup Low Battery Threshold */
 474        if (cs35l32->pdata.batt_thresh)
 475                regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD,
 476                                   CS35L32_BATT_THRESH_MASK,
 477                                cs35l32->pdata.batt_thresh << 4);
 478
 479        /* Power down the AMP */
 480        regmap_update_bits(cs35l32->regmap, CS35L32_PWRCTL1, CS35L32_PDN_AMP,
 481                            CS35L32_PDN_AMP);
 482
 483        /* Clear MCLK Error Bit since we don't have the clock yet */
 484        regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, &reg);
 485
 486        ret = devm_snd_soc_register_component(&i2c_client->dev,
 487                        &soc_component_dev_cs35l32, cs35l32_dai,
 488                        ARRAY_SIZE(cs35l32_dai));
 489        if (ret < 0)
 490                goto err_disable;
 491
 492        return 0;
 493
 494err_disable:
 495        gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
 496err_supplies:
 497        regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies),
 498                               cs35l32->supplies);
 499        return ret;
 500}
 501
 502static int cs35l32_i2c_remove(struct i2c_client *i2c_client)
 503{
 504        struct cs35l32_private *cs35l32 = i2c_get_clientdata(i2c_client);
 505
 506        /* Hold down reset */
 507        gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
 508
 509        return 0;
 510}
 511
 512#ifdef CONFIG_PM
 513static int cs35l32_runtime_suspend(struct device *dev)
 514{
 515        struct cs35l32_private *cs35l32 = dev_get_drvdata(dev);
 516
 517        regcache_cache_only(cs35l32->regmap, true);
 518        regcache_mark_dirty(cs35l32->regmap);
 519
 520        /* Hold down reset */
 521        gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
 522
 523        /* remove power */
 524        regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies),
 525                               cs35l32->supplies);
 526
 527        return 0;
 528}
 529
 530static int cs35l32_runtime_resume(struct device *dev)
 531{
 532        struct cs35l32_private *cs35l32 = dev_get_drvdata(dev);
 533        int ret;
 534
 535        /* Enable power */
 536        ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies),
 537                                    cs35l32->supplies);
 538        if (ret != 0) {
 539                dev_err(dev, "Failed to enable supplies: %d\n",
 540                        ret);
 541                return ret;
 542        }
 543
 544        gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
 545
 546        regcache_cache_only(cs35l32->regmap, false);
 547        regcache_sync(cs35l32->regmap);
 548
 549        return 0;
 550}
 551#endif
 552
 553static const struct dev_pm_ops cs35l32_runtime_pm = {
 554        SET_RUNTIME_PM_OPS(cs35l32_runtime_suspend, cs35l32_runtime_resume,
 555                           NULL)
 556};
 557
 558static const struct of_device_id cs35l32_of_match[] = {
 559        { .compatible = "cirrus,cs35l32", },
 560        {},
 561};
 562MODULE_DEVICE_TABLE(of, cs35l32_of_match);
 563
 564
 565static const struct i2c_device_id cs35l32_id[] = {
 566        {"cs35l32", 0},
 567        {}
 568};
 569
 570MODULE_DEVICE_TABLE(i2c, cs35l32_id);
 571
 572static struct i2c_driver cs35l32_i2c_driver = {
 573        .driver = {
 574                   .name = "cs35l32",
 575                   .pm = &cs35l32_runtime_pm,
 576                   .of_match_table = cs35l32_of_match,
 577                   },
 578        .id_table = cs35l32_id,
 579        .probe = cs35l32_i2c_probe,
 580        .remove = cs35l32_i2c_remove,
 581};
 582
 583module_i2c_driver(cs35l32_i2c_driver);
 584
 585MODULE_DESCRIPTION("ASoC CS35L32 driver");
 586MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
 587MODULE_LICENSE("GPL");
 588