linux/sound/aoa/fabrics/layout.c
<<
>>
Prefs
   1/*
   2 * Apple Onboard Audio driver -- layout/machine id fabric
   3 *
   4 * Copyright 2006-2008 Johannes Berg <johannes@sipsolutions.net>
   5 *
   6 * GPL v2, can be found in COPYING.
   7 *
   8 *
   9 * This fabric module looks for sound codecs based on the
  10 * layout-id or device-id property in the device tree.
  11 */
  12#include <asm/prom.h>
  13#include <linux/list.h>
  14#include <linux/module.h>
  15#include <linux/slab.h>
  16#include "../aoa.h"
  17#include "../soundbus/soundbus.h"
  18
  19MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
  20MODULE_LICENSE("GPL");
  21MODULE_DESCRIPTION("Layout-ID fabric for snd-aoa");
  22
  23#define MAX_CODECS_PER_BUS      2
  24
  25/* These are the connections the layout fabric
  26 * knows about. It doesn't really care about the
  27 * input ones, but I thought I'd separate them
  28 * to give them proper names. The thing is that
  29 * Apple usually will distinguish the active output
  30 * by GPIOs, while the active input is set directly
  31 * on the codec. Hence we here tell the codec what
  32 * we think is connected. This information is hard-
  33 * coded below ... */
  34#define CC_SPEAKERS     (1<<0)
  35#define CC_HEADPHONE    (1<<1)
  36#define CC_LINEOUT      (1<<2)
  37#define CC_DIGITALOUT   (1<<3)
  38#define CC_LINEIN       (1<<4)
  39#define CC_MICROPHONE   (1<<5)
  40#define CC_DIGITALIN    (1<<6)
  41/* pretty bogus but users complain...
  42 * This is a flag saying that the LINEOUT
  43 * should be renamed to HEADPHONE.
  44 * be careful with input detection! */
  45#define CC_LINEOUT_LABELLED_HEADPHONE   (1<<7)
  46
  47struct codec_connection {
  48        /* CC_ flags from above */
  49        int connected;
  50        /* codec dependent bit to be set in the aoa_codec.connected field.
  51         * This intentionally doesn't have any generic flags because the
  52         * fabric has to know the codec anyway and all codecs might have
  53         * different connectors */
  54        int codec_bit;
  55};
  56
  57struct codec_connect_info {
  58        char *name;
  59        struct codec_connection *connections;
  60};
  61
  62#define LAYOUT_FLAG_COMBO_LINEOUT_SPDIF (1<<0)
  63
  64struct layout {
  65        unsigned int layout_id, device_id;
  66        struct codec_connect_info codecs[MAX_CODECS_PER_BUS];
  67        int flags;
  68
  69        /* if busname is not assigned, we use 'Master' below,
  70         * so that our layout table doesn't need to be filled
  71         * too much.
  72         * We only assign these two if we expect to find more
  73         * than one soundbus, i.e. on those machines with
  74         * multiple layout-ids */
  75        char *busname;
  76        int pcmid;
  77};
  78
  79MODULE_ALIAS("sound-layout-36");
  80MODULE_ALIAS("sound-layout-41");
  81MODULE_ALIAS("sound-layout-45");
  82MODULE_ALIAS("sound-layout-47");
  83MODULE_ALIAS("sound-layout-48");
  84MODULE_ALIAS("sound-layout-49");
  85MODULE_ALIAS("sound-layout-50");
  86MODULE_ALIAS("sound-layout-51");
  87MODULE_ALIAS("sound-layout-56");
  88MODULE_ALIAS("sound-layout-57");
  89MODULE_ALIAS("sound-layout-58");
  90MODULE_ALIAS("sound-layout-60");
  91MODULE_ALIAS("sound-layout-61");
  92MODULE_ALIAS("sound-layout-62");
  93MODULE_ALIAS("sound-layout-64");
  94MODULE_ALIAS("sound-layout-65");
  95MODULE_ALIAS("sound-layout-66");
  96MODULE_ALIAS("sound-layout-67");
  97MODULE_ALIAS("sound-layout-68");
  98MODULE_ALIAS("sound-layout-69");
  99MODULE_ALIAS("sound-layout-70");
 100MODULE_ALIAS("sound-layout-72");
 101MODULE_ALIAS("sound-layout-76");
 102MODULE_ALIAS("sound-layout-80");
 103MODULE_ALIAS("sound-layout-82");
 104MODULE_ALIAS("sound-layout-84");
 105MODULE_ALIAS("sound-layout-86");
 106MODULE_ALIAS("sound-layout-90");
 107MODULE_ALIAS("sound-layout-92");
 108MODULE_ALIAS("sound-layout-94");
 109MODULE_ALIAS("sound-layout-96");
 110MODULE_ALIAS("sound-layout-98");
 111MODULE_ALIAS("sound-layout-100");
 112
 113MODULE_ALIAS("aoa-device-id-14");
 114MODULE_ALIAS("aoa-device-id-22");
 115MODULE_ALIAS("aoa-device-id-35");
 116
 117/* onyx with all but microphone connected */
 118static struct codec_connection onyx_connections_nomic[] = {
 119        {
 120                .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
 121                .codec_bit = 0,
 122        },
 123        {
 124                .connected = CC_DIGITALOUT,
 125                .codec_bit = 1,
 126        },
 127        {
 128                .connected = CC_LINEIN,
 129                .codec_bit = 2,
 130        },
 131        {} /* terminate array by .connected == 0 */
 132};
 133
 134/* onyx on machines without headphone */
 135static struct codec_connection onyx_connections_noheadphones[] = {
 136        {
 137                .connected = CC_SPEAKERS | CC_LINEOUT |
 138                             CC_LINEOUT_LABELLED_HEADPHONE,
 139                .codec_bit = 0,
 140        },
 141        {
 142                .connected = CC_DIGITALOUT,
 143                .codec_bit = 1,
 144        },
 145        /* FIXME: are these correct? probably not for all the machines
 146         * below ... If not this will need separating. */
 147        {
 148                .connected = CC_LINEIN,
 149                .codec_bit = 2,
 150        },
 151        {
 152                .connected = CC_MICROPHONE,
 153                .codec_bit = 3,
 154        },
 155        {} /* terminate array by .connected == 0 */
 156};
 157
 158/* onyx on machines with real line-out */
 159static struct codec_connection onyx_connections_reallineout[] = {
 160        {
 161                .connected = CC_SPEAKERS | CC_LINEOUT | CC_HEADPHONE,
 162                .codec_bit = 0,
 163        },
 164        {
 165                .connected = CC_DIGITALOUT,
 166                .codec_bit = 1,
 167        },
 168        {
 169                .connected = CC_LINEIN,
 170                .codec_bit = 2,
 171        },
 172        {} /* terminate array by .connected == 0 */
 173};
 174
 175/* tas on machines without line out */
 176static struct codec_connection tas_connections_nolineout[] = {
 177        {
 178                .connected = CC_SPEAKERS | CC_HEADPHONE,
 179                .codec_bit = 0,
 180        },
 181        {
 182                .connected = CC_LINEIN,
 183                .codec_bit = 2,
 184        },
 185        {
 186                .connected = CC_MICROPHONE,
 187                .codec_bit = 3,
 188        },
 189        {} /* terminate array by .connected == 0 */
 190};
 191
 192/* tas on machines with neither line out nor line in */
 193static struct codec_connection tas_connections_noline[] = {
 194        {
 195                .connected = CC_SPEAKERS | CC_HEADPHONE,
 196                .codec_bit = 0,
 197        },
 198        {
 199                .connected = CC_MICROPHONE,
 200                .codec_bit = 3,
 201        },
 202        {} /* terminate array by .connected == 0 */
 203};
 204
 205/* tas on machines without microphone */
 206static struct codec_connection tas_connections_nomic[] = {
 207        {
 208                .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
 209                .codec_bit = 0,
 210        },
 211        {
 212                .connected = CC_LINEIN,
 213                .codec_bit = 2,
 214        },
 215        {} /* terminate array by .connected == 0 */
 216};
 217
 218/* tas on machines with everything connected */
 219static struct codec_connection tas_connections_all[] = {
 220        {
 221                .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
 222                .codec_bit = 0,
 223        },
 224        {
 225                .connected = CC_LINEIN,
 226                .codec_bit = 2,
 227        },
 228        {
 229                .connected = CC_MICROPHONE,
 230                .codec_bit = 3,
 231        },
 232        {} /* terminate array by .connected == 0 */
 233};
 234
 235static struct codec_connection toonie_connections[] = {
 236        {
 237                .connected = CC_SPEAKERS | CC_HEADPHONE,
 238                .codec_bit = 0,
 239        },
 240        {} /* terminate array by .connected == 0 */
 241};
 242
 243static struct codec_connection topaz_input[] = {
 244        {
 245                .connected = CC_DIGITALIN,
 246                .codec_bit = 0,
 247        },
 248        {} /* terminate array by .connected == 0 */
 249};
 250
 251static struct codec_connection topaz_output[] = {
 252        {
 253                .connected = CC_DIGITALOUT,
 254                .codec_bit = 1,
 255        },
 256        {} /* terminate array by .connected == 0 */
 257};
 258
 259static struct codec_connection topaz_inout[] = {
 260        {
 261                .connected = CC_DIGITALIN,
 262                .codec_bit = 0,
 263        },
 264        {
 265                .connected = CC_DIGITALOUT,
 266                .codec_bit = 1,
 267        },
 268        {} /* terminate array by .connected == 0 */
 269};
 270
 271static struct layout layouts[] = {
 272        /* last PowerBooks (15" Oct 2005) */
 273        { .layout_id = 82,
 274          .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
 275          .codecs[0] = {
 276                .name = "onyx",
 277                .connections = onyx_connections_noheadphones,
 278          },
 279          .codecs[1] = {
 280                .name = "topaz",
 281                .connections = topaz_input,
 282          },
 283        },
 284        /* PowerMac9,1 */
 285        { .layout_id = 60,
 286          .codecs[0] = {
 287                .name = "onyx",
 288                .connections = onyx_connections_reallineout,
 289          },
 290        },
 291        /* PowerMac9,1 */
 292        { .layout_id = 61,
 293          .codecs[0] = {
 294                .name = "topaz",
 295                .connections = topaz_input,
 296          },
 297        },
 298        /* PowerBook5,7 */
 299        { .layout_id = 64,
 300          .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
 301          .codecs[0] = {
 302                .name = "onyx",
 303                .connections = onyx_connections_noheadphones,
 304          },
 305        },
 306        /* PowerBook5,7 */
 307        { .layout_id = 65,
 308          .codecs[0] = {
 309                .name = "topaz",
 310                .connections = topaz_input,
 311          },
 312        },
 313        /* PowerBook5,9 [17" Oct 2005] */
 314        { .layout_id = 84,
 315          .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
 316          .codecs[0] = {
 317                .name = "onyx",
 318                .connections = onyx_connections_noheadphones,
 319          },
 320          .codecs[1] = {
 321                .name = "topaz",
 322                .connections = topaz_input,
 323          },
 324        },
 325        /* PowerMac8,1 */
 326        { .layout_id = 45,
 327          .codecs[0] = {
 328                .name = "onyx",
 329                .connections = onyx_connections_noheadphones,
 330          },
 331          .codecs[1] = {
 332                .name = "topaz",
 333                .connections = topaz_input,
 334          },
 335        },
 336        /* Quad PowerMac (analog in, analog/digital out) */
 337        { .layout_id = 68,
 338          .codecs[0] = {
 339                .name = "onyx",
 340                .connections = onyx_connections_nomic,
 341          },
 342        },
 343        /* Quad PowerMac (digital in) */
 344        { .layout_id = 69,
 345          .codecs[0] = {
 346                .name = "topaz",
 347                .connections = topaz_input,
 348          },
 349          .busname = "digital in", .pcmid = 1 },
 350        /* Early 2005 PowerBook (PowerBook 5,6) */
 351        { .layout_id = 70,
 352          .codecs[0] = {
 353                .name = "tas",
 354                .connections = tas_connections_nolineout,
 355          },
 356        },
 357        /* PowerBook 5,4 */
 358        { .layout_id = 51,
 359          .codecs[0] = {
 360                .name = "tas",
 361                .connections = tas_connections_nolineout,
 362          },
 363        },
 364        /* PowerBook6,7 */
 365        { .layout_id = 80,
 366          .codecs[0] = {
 367                .name = "tas",
 368                .connections = tas_connections_noline,
 369          },
 370        },
 371        /* PowerBook6,8 */
 372        { .layout_id = 72,
 373          .codecs[0] = {
 374                .name = "tas",
 375                .connections = tas_connections_nolineout,
 376          },
 377        },
 378        /* PowerMac8,2 */
 379        { .layout_id = 86,
 380          .codecs[0] = {
 381                .name = "onyx",
 382                .connections = onyx_connections_nomic,
 383          },
 384          .codecs[1] = {
 385                .name = "topaz",
 386                .connections = topaz_input,
 387          },
 388        },
 389        /* PowerBook6,7 */
 390        { .layout_id = 92,
 391          .codecs[0] = {
 392                .name = "tas",
 393                .connections = tas_connections_nolineout,
 394          },
 395        },
 396        /* PowerMac10,1 (Mac Mini) */
 397        { .layout_id = 58,
 398          .codecs[0] = {
 399                .name = "toonie",
 400                .connections = toonie_connections,
 401          },
 402        },
 403        {
 404          .layout_id = 96,
 405          .codecs[0] = {
 406                .name = "onyx",
 407                .connections = onyx_connections_noheadphones,
 408          },
 409        },
 410        /* unknown, untested, but this comes from Apple */
 411        { .layout_id = 41,
 412          .codecs[0] = {
 413                .name = "tas",
 414                .connections = tas_connections_all,
 415          },
 416        },
 417        { .layout_id = 36,
 418          .codecs[0] = {
 419                .name = "tas",
 420                .connections = tas_connections_nomic,
 421          },
 422          .codecs[1] = {
 423                .name = "topaz",
 424                .connections = topaz_inout,
 425          },
 426        },
 427        { .layout_id = 47,
 428          .codecs[0] = {
 429                .name = "onyx",
 430                .connections = onyx_connections_noheadphones,
 431          },
 432        },
 433        { .layout_id = 48,
 434          .codecs[0] = {
 435                .name = "topaz",
 436                .connections = topaz_input,
 437          },
 438        },
 439        { .layout_id = 49,
 440          .codecs[0] = {
 441                .name = "onyx",
 442                .connections = onyx_connections_nomic,
 443          },
 444        },
 445        { .layout_id = 50,
 446          .codecs[0] = {
 447                .name = "topaz",
 448                .connections = topaz_input,
 449          },
 450        },
 451        { .layout_id = 56,
 452          .codecs[0] = {
 453                .name = "onyx",
 454                .connections = onyx_connections_noheadphones,
 455          },
 456        },
 457        { .layout_id = 57,
 458          .codecs[0] = {
 459                .name = "topaz",
 460                .connections = topaz_input,
 461          },
 462        },
 463        { .layout_id = 62,
 464          .codecs[0] = {
 465                .name = "onyx",
 466                .connections = onyx_connections_noheadphones,
 467          },
 468          .codecs[1] = {
 469                .name = "topaz",
 470                .connections = topaz_output,
 471          },
 472        },
 473        { .layout_id = 66,
 474          .codecs[0] = {
 475                .name = "onyx",
 476                .connections = onyx_connections_noheadphones,
 477          },
 478        },
 479        { .layout_id = 67,
 480          .codecs[0] = {
 481                .name = "topaz",
 482                .connections = topaz_input,
 483          },
 484        },
 485        { .layout_id = 76,
 486          .codecs[0] = {
 487                .name = "tas",
 488                .connections = tas_connections_nomic,
 489          },
 490          .codecs[1] = {
 491                .name = "topaz",
 492                .connections = topaz_inout,
 493          },
 494        },
 495        { .layout_id = 90,
 496          .codecs[0] = {
 497                .name = "tas",
 498                .connections = tas_connections_noline,
 499          },
 500        },
 501        { .layout_id = 94,
 502          .codecs[0] = {
 503                .name = "onyx",
 504                /* but it has an external mic?? how to select? */
 505                .connections = onyx_connections_noheadphones,
 506          },
 507        },
 508        { .layout_id = 98,
 509          .codecs[0] = {
 510                .name = "toonie",
 511                .connections = toonie_connections,
 512          },
 513        },
 514        { .layout_id = 100,
 515          .codecs[0] = {
 516                .name = "topaz",
 517                .connections = topaz_input,
 518          },
 519          .codecs[1] = {
 520                .name = "onyx",
 521                .connections = onyx_connections_noheadphones,
 522          },
 523        },
 524        /* PowerMac3,4 */
 525        { .device_id = 14,
 526          .codecs[0] = {
 527                .name = "tas",
 528                .connections = tas_connections_noline,
 529          },
 530        },
 531        /* PowerMac3,6 */
 532        { .device_id = 22,
 533          .codecs[0] = {
 534                .name = "tas",
 535                .connections = tas_connections_all,
 536          },
 537        },
 538        /* PowerBook5,2 */
 539        { .device_id = 35,
 540          .codecs[0] = {
 541                .name = "tas",
 542                .connections = tas_connections_all,
 543          },
 544        },
 545        {}
 546};
 547
 548static struct layout *find_layout_by_id(unsigned int id)
 549{
 550        struct layout *l;
 551
 552        l = layouts;
 553        while (l->codecs[0].name) {
 554                if (l->layout_id == id)
 555                        return l;
 556                l++;
 557        }
 558        return NULL;
 559}
 560
 561static struct layout *find_layout_by_device(unsigned int id)
 562{
 563        struct layout *l;
 564
 565        l = layouts;
 566        while (l->codecs[0].name) {
 567                if (l->device_id == id)
 568                        return l;
 569                l++;
 570        }
 571        return NULL;
 572}
 573
 574static void use_layout(struct layout *l)
 575{
 576        int i;
 577
 578        for (i=0; i<MAX_CODECS_PER_BUS; i++) {
 579                if (l->codecs[i].name) {
 580                        request_module("snd-aoa-codec-%s", l->codecs[i].name);
 581                }
 582        }
 583        /* now we wait for the codecs to call us back */
 584}
 585
 586struct layout_dev;
 587
 588struct layout_dev_ptr {
 589        struct layout_dev *ptr;
 590};
 591
 592struct layout_dev {
 593        struct list_head list;
 594        struct soundbus_dev *sdev;
 595        struct device_node *sound;
 596        struct aoa_codec *codecs[MAX_CODECS_PER_BUS];
 597        struct layout *layout;
 598        struct gpio_runtime gpio;
 599
 600        /* we need these for headphone/lineout detection */
 601        struct snd_kcontrol *headphone_ctrl;
 602        struct snd_kcontrol *lineout_ctrl;
 603        struct snd_kcontrol *speaker_ctrl;
 604        struct snd_kcontrol *master_ctrl;
 605        struct snd_kcontrol *headphone_detected_ctrl;
 606        struct snd_kcontrol *lineout_detected_ctrl;
 607
 608        struct layout_dev_ptr selfptr_headphone;
 609        struct layout_dev_ptr selfptr_lineout;
 610
 611        u32 have_lineout_detect:1,
 612            have_headphone_detect:1,
 613            switch_on_headphone:1,
 614            switch_on_lineout:1;
 615};
 616
 617static LIST_HEAD(layouts_list);
 618static int layouts_list_items;
 619/* this can go away but only if we allow multiple cards,
 620 * make the fabric handle all the card stuff, etc... */
 621static struct layout_dev *layout_device;
 622
 623#define control_info    snd_ctl_boolean_mono_info
 624
 625#define AMP_CONTROL(n, description)                                     \
 626static int n##_control_get(struct snd_kcontrol *kcontrol,               \
 627                           struct snd_ctl_elem_value *ucontrol)         \
 628{                                                                       \
 629        struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol);        \
 630        if (gpio->methods && gpio->methods->get_##n)                    \
 631                ucontrol->value.integer.value[0] =                      \
 632                        gpio->methods->get_##n(gpio);                   \
 633        return 0;                                                       \
 634}                                                                       \
 635static int n##_control_put(struct snd_kcontrol *kcontrol,               \
 636                           struct snd_ctl_elem_value *ucontrol)         \
 637{                                                                       \
 638        struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol);        \
 639        if (gpio->methods && gpio->methods->get_##n)                    \
 640                gpio->methods->set_##n(gpio,                            \
 641                        !!ucontrol->value.integer.value[0]);            \
 642        return 1;                                                       \
 643}                                                                       \
 644static struct snd_kcontrol_new n##_ctl = {                              \
 645        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,                            \
 646        .name = description,                                            \
 647        .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,                      \
 648        .info = control_info,                                           \
 649        .get = n##_control_get,                                         \
 650        .put = n##_control_put,                                         \
 651}
 652
 653AMP_CONTROL(headphone, "Headphone Switch");
 654AMP_CONTROL(speakers, "Speakers Switch");
 655AMP_CONTROL(lineout, "Line-Out Switch");
 656AMP_CONTROL(master, "Master Switch");
 657
 658static int detect_choice_get(struct snd_kcontrol *kcontrol,
 659                             struct snd_ctl_elem_value *ucontrol)
 660{
 661        struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);
 662
 663        switch (kcontrol->private_value) {
 664        case 0:
 665                ucontrol->value.integer.value[0] = ldev->switch_on_headphone;
 666                break;
 667        case 1:
 668                ucontrol->value.integer.value[0] = ldev->switch_on_lineout;
 669                break;
 670        default:
 671                return -ENODEV;
 672        }
 673        return 0;
 674}
 675
 676static int detect_choice_put(struct snd_kcontrol *kcontrol,
 677                             struct snd_ctl_elem_value *ucontrol)
 678{
 679        struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);
 680
 681        switch (kcontrol->private_value) {
 682        case 0:
 683                ldev->switch_on_headphone = !!ucontrol->value.integer.value[0];
 684                break;
 685        case 1:
 686                ldev->switch_on_lineout = !!ucontrol->value.integer.value[0];
 687                break;
 688        default:
 689                return -ENODEV;
 690        }
 691        return 1;
 692}
 693
 694static struct snd_kcontrol_new headphone_detect_choice = {
 695        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 696        .name = "Headphone Detect Autoswitch",
 697        .info = control_info,
 698        .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 699        .get = detect_choice_get,
 700        .put = detect_choice_put,
 701        .private_value = 0,
 702};
 703
 704static struct snd_kcontrol_new lineout_detect_choice = {
 705        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 706        .name = "Line-Out Detect Autoswitch",
 707        .info = control_info,
 708        .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 709        .get = detect_choice_get,
 710        .put = detect_choice_put,
 711        .private_value = 1,
 712};
 713
 714static int detected_get(struct snd_kcontrol *kcontrol,
 715                        struct snd_ctl_elem_value *ucontrol)
 716{
 717        struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);
 718        int v;
 719
 720        switch (kcontrol->private_value) {
 721        case 0:
 722                v = ldev->gpio.methods->get_detect(&ldev->gpio,
 723                                                   AOA_NOTIFY_HEADPHONE);
 724                break;
 725        case 1:
 726                v = ldev->gpio.methods->get_detect(&ldev->gpio,
 727                                                   AOA_NOTIFY_LINE_OUT);
 728                break;
 729        default:
 730                return -ENODEV;
 731        }
 732        ucontrol->value.integer.value[0] = v;
 733        return 0;
 734}
 735
 736static struct snd_kcontrol_new headphone_detected = {
 737        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 738        .name = "Headphone Detected",
 739        .info = control_info,
 740        .access = SNDRV_CTL_ELEM_ACCESS_READ,
 741        .get = detected_get,
 742        .private_value = 0,
 743};
 744
 745static struct snd_kcontrol_new lineout_detected = {
 746        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 747        .name = "Line-Out Detected",
 748        .info = control_info,
 749        .access = SNDRV_CTL_ELEM_ACCESS_READ,
 750        .get = detected_get,
 751        .private_value = 1,
 752};
 753
 754static int check_codec(struct aoa_codec *codec,
 755                       struct layout_dev *ldev,
 756                       struct codec_connect_info *cci)
 757{
 758        const u32 *ref;
 759        char propname[32];
 760        struct codec_connection *cc;
 761
 762        /* if the codec has a 'codec' node, we require a reference */
 763        if (codec->node && (strcmp(codec->node->name, "codec") == 0)) {
 764                snprintf(propname, sizeof(propname),
 765                         "platform-%s-codec-ref", codec->name);
 766                ref = of_get_property(ldev->sound, propname, NULL);
 767                if (!ref) {
 768                        printk(KERN_INFO "snd-aoa-fabric-layout: "
 769                                "required property %s not present\n", propname);
 770                        return -ENODEV;
 771                }
 772                if (*ref != codec->node->phandle) {
 773                        printk(KERN_INFO "snd-aoa-fabric-layout: "
 774                                "%s doesn't match!\n", propname);
 775                        return -ENODEV;
 776                }
 777        } else {
 778                if (layouts_list_items != 1) {
 779                        printk(KERN_INFO "snd-aoa-fabric-layout: "
 780                                "more than one soundbus, but no references.\n");
 781                        return -ENODEV;
 782                }
 783        }
 784        codec->soundbus_dev = ldev->sdev;
 785        codec->gpio = &ldev->gpio;
 786
 787        cc = cci->connections;
 788        if (!cc)
 789                return -EINVAL;
 790
 791        printk(KERN_INFO "snd-aoa-fabric-layout: can use this codec\n");
 792
 793        codec->connected = 0;
 794        codec->fabric_data = cc;
 795
 796        while (cc->connected) {
 797                codec->connected |= 1<<cc->codec_bit;
 798                cc++;
 799        }
 800
 801        return 0;
 802}
 803
 804static int layout_found_codec(struct aoa_codec *codec)
 805{
 806        struct layout_dev *ldev;
 807        int i;
 808
 809        list_for_each_entry(ldev, &layouts_list, list) {
 810                for (i=0; i<MAX_CODECS_PER_BUS; i++) {
 811                        if (!ldev->layout->codecs[i].name)
 812                                continue;
 813                        if (strcmp(ldev->layout->codecs[i].name, codec->name) == 0) {
 814                                if (check_codec(codec,
 815                                                ldev,
 816                                                &ldev->layout->codecs[i]) == 0)
 817                                        return 0;
 818                        }
 819                }
 820        }
 821        return -ENODEV;
 822}
 823
 824static void layout_remove_codec(struct aoa_codec *codec)
 825{
 826        int i;
 827        /* here remove the codec from the layout dev's
 828         * codec reference */
 829
 830        codec->soundbus_dev = NULL;
 831        codec->gpio = NULL;
 832        for (i=0; i<MAX_CODECS_PER_BUS; i++) {
 833        }
 834}
 835
 836static void layout_notify(void *data)
 837{
 838        struct layout_dev_ptr *dptr = data;
 839        struct layout_dev *ldev;
 840        int v, update;
 841        struct snd_kcontrol *detected, *c;
 842        struct snd_card *card = aoa_get_card();
 843
 844        ldev = dptr->ptr;
 845        if (data == &ldev->selfptr_headphone) {
 846                v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_HEADPHONE);
 847                detected = ldev->headphone_detected_ctrl;
 848                update = ldev->switch_on_headphone;
 849                if (update) {
 850                        ldev->gpio.methods->set_speakers(&ldev->gpio, !v);
 851                        ldev->gpio.methods->set_headphone(&ldev->gpio, v);
 852                        ldev->gpio.methods->set_lineout(&ldev->gpio, 0);
 853                }
 854        } else if (data == &ldev->selfptr_lineout) {
 855                v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_LINE_OUT);
 856                detected = ldev->lineout_detected_ctrl;
 857                update = ldev->switch_on_lineout;
 858                if (update) {
 859                        ldev->gpio.methods->set_speakers(&ldev->gpio, !v);
 860                        ldev->gpio.methods->set_headphone(&ldev->gpio, 0);
 861                        ldev->gpio.methods->set_lineout(&ldev->gpio, v);
 862                }
 863        } else
 864                return;
 865
 866        if (detected)
 867                snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &detected->id);
 868        if (update) {
 869                c = ldev->headphone_ctrl;
 870                if (c)
 871                        snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
 872                c = ldev->speaker_ctrl;
 873                if (c)
 874                        snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
 875                c = ldev->lineout_ctrl;
 876                if (c)
 877                        snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
 878        }
 879}
 880
 881static void layout_attached_codec(struct aoa_codec *codec)
 882{
 883        struct codec_connection *cc;
 884        struct snd_kcontrol *ctl;
 885        int headphones, lineout;
 886        struct layout_dev *ldev = layout_device;
 887
 888        /* need to add this codec to our codec array! */
 889
 890        cc = codec->fabric_data;
 891
 892        headphones = codec->gpio->methods->get_detect(codec->gpio,
 893                                                      AOA_NOTIFY_HEADPHONE);
 894        lineout = codec->gpio->methods->get_detect(codec->gpio,
 895                                                   AOA_NOTIFY_LINE_OUT);
 896
 897        if (codec->gpio->methods->set_master) {
 898                ctl = snd_ctl_new1(&master_ctl, codec->gpio);
 899                ldev->master_ctrl = ctl;
 900                aoa_snd_ctl_add(ctl);
 901        }
 902        while (cc->connected) {
 903                if (cc->connected & CC_SPEAKERS) {
 904                        if (headphones <= 0 && lineout <= 0)
 905                                ldev->gpio.methods->set_speakers(codec->gpio, 1);
 906                        ctl = snd_ctl_new1(&speakers_ctl, codec->gpio);
 907                        ldev->speaker_ctrl = ctl;
 908                        aoa_snd_ctl_add(ctl);
 909                }
 910                if (cc->connected & CC_HEADPHONE) {
 911                        if (headphones == 1)
 912                                ldev->gpio.methods->set_headphone(codec->gpio, 1);
 913                        ctl = snd_ctl_new1(&headphone_ctl, codec->gpio);
 914                        ldev->headphone_ctrl = ctl;
 915                        aoa_snd_ctl_add(ctl);
 916                        ldev->have_headphone_detect =
 917                                !ldev->gpio.methods
 918                                        ->set_notify(&ldev->gpio,
 919                                                     AOA_NOTIFY_HEADPHONE,
 920                                                     layout_notify,
 921                                                     &ldev->selfptr_headphone);
 922                        if (ldev->have_headphone_detect) {
 923                                ctl = snd_ctl_new1(&headphone_detect_choice,
 924                                                   ldev);
 925                                aoa_snd_ctl_add(ctl);
 926                                ctl = snd_ctl_new1(&headphone_detected,
 927                                                   ldev);
 928                                ldev->headphone_detected_ctrl = ctl;
 929                                aoa_snd_ctl_add(ctl);
 930                        }
 931                }
 932                if (cc->connected & CC_LINEOUT) {
 933                        if (lineout == 1)
 934                                ldev->gpio.methods->set_lineout(codec->gpio, 1);
 935                        ctl = snd_ctl_new1(&lineout_ctl, codec->gpio);
 936                        if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
 937                                strlcpy(ctl->id.name,
 938                                        "Headphone Switch", sizeof(ctl->id.name));
 939                        ldev->lineout_ctrl = ctl;
 940                        aoa_snd_ctl_add(ctl);
 941                        ldev->have_lineout_detect =
 942                                !ldev->gpio.methods
 943                                        ->set_notify(&ldev->gpio,
 944                                                     AOA_NOTIFY_LINE_OUT,
 945                                                     layout_notify,
 946                                                     &ldev->selfptr_lineout);
 947                        if (ldev->have_lineout_detect) {
 948                                ctl = snd_ctl_new1(&lineout_detect_choice,
 949                                                   ldev);
 950                                if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
 951                                        strlcpy(ctl->id.name,
 952                                                "Headphone Detect Autoswitch",
 953                                                sizeof(ctl->id.name));
 954                                aoa_snd_ctl_add(ctl);
 955                                ctl = snd_ctl_new1(&lineout_detected,
 956                                                   ldev);
 957                                if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
 958                                        strlcpy(ctl->id.name,
 959                                                "Headphone Detected",
 960                                                sizeof(ctl->id.name));
 961                                ldev->lineout_detected_ctrl = ctl;
 962                                aoa_snd_ctl_add(ctl);
 963                        }
 964                }
 965                cc++;
 966        }
 967        /* now update initial state */
 968        if (ldev->have_headphone_detect)
 969                layout_notify(&ldev->selfptr_headphone);
 970        if (ldev->have_lineout_detect)
 971                layout_notify(&ldev->selfptr_lineout);
 972}
 973
 974static struct aoa_fabric layout_fabric = {
 975        .name = "SoundByLayout",
 976        .owner = THIS_MODULE,
 977        .found_codec = layout_found_codec,
 978        .remove_codec = layout_remove_codec,
 979        .attached_codec = layout_attached_codec,
 980};
 981
 982static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
 983{
 984        struct device_node *sound = NULL;
 985        const unsigned int *id;
 986        struct layout *layout = NULL;
 987        struct layout_dev *ldev = NULL;
 988        int err;
 989
 990        /* hm, currently we can only have one ... */
 991        if (layout_device)
 992                return -ENODEV;
 993
 994        /* by breaking out we keep a reference */
 995        while ((sound = of_get_next_child(sdev->ofdev.dev.of_node, sound))) {
 996                if (sound->type && strcasecmp(sound->type, "soundchip") == 0)
 997                        break;
 998        }
 999        if (!sound)
1000                return -ENODEV;
1001
1002        id = of_get_property(sound, "layout-id", NULL);
1003        if (id) {
1004                layout = find_layout_by_id(*id);
1005        } else {
1006                id = of_get_property(sound, "device-id", NULL);
1007                if (id)
1008                        layout = find_layout_by_device(*id);
1009        }
1010
1011        if (!layout) {
1012                printk(KERN_ERR "snd-aoa-fabric-layout: unknown layout\n");
1013                goto outnodev;
1014        }
1015
1016        ldev = kzalloc(sizeof(struct layout_dev), GFP_KERNEL);
1017        if (!ldev)
1018                goto outnodev;
1019
1020        layout_device = ldev;
1021        ldev->sdev = sdev;
1022        ldev->sound = sound;
1023        ldev->layout = layout;
1024        ldev->gpio.node = sound->parent;
1025        switch (layout->layout_id) {
1026        case 0:  /* anything with device_id, not layout_id */
1027        case 41: /* that unknown machine no one seems to have */
1028        case 51: /* PowerBook5,4 */
1029        case 58: /* Mac Mini */
1030                ldev->gpio.methods = ftr_gpio_methods;
1031                printk(KERN_DEBUG
1032                       "snd-aoa-fabric-layout: Using direct GPIOs\n");
1033                break;
1034        default:
1035                ldev->gpio.methods = pmf_gpio_methods;
1036                printk(KERN_DEBUG
1037                       "snd-aoa-fabric-layout: Using PMF GPIOs\n");
1038        }
1039        ldev->selfptr_headphone.ptr = ldev;
1040        ldev->selfptr_lineout.ptr = ldev;
1041        dev_set_drvdata(&sdev->ofdev.dev, ldev);
1042        list_add(&ldev->list, &layouts_list);
1043        layouts_list_items++;
1044
1045        /* assign these before registering ourselves, so
1046         * callbacks that are done during registration
1047         * already have the values */
1048        sdev->pcmid = ldev->layout->pcmid;
1049        if (ldev->layout->busname) {
1050                sdev->pcmname = ldev->layout->busname;
1051        } else {
1052                sdev->pcmname = "Master";
1053        }
1054
1055        ldev->gpio.methods->init(&ldev->gpio);
1056
1057        err = aoa_fabric_register(&layout_fabric, &sdev->ofdev.dev);
1058        if (err && err != -EALREADY) {
1059                printk(KERN_INFO "snd-aoa-fabric-layout: can't use,"
1060                                 " another fabric is active!\n");
1061                goto outlistdel;
1062        }
1063
1064        use_layout(layout);
1065        ldev->switch_on_headphone = 1;
1066        ldev->switch_on_lineout = 1;
1067        return 0;
1068 outlistdel:
1069        /* we won't be using these then... */
1070        ldev->gpio.methods->exit(&ldev->gpio);
1071        /* reset if we didn't use it */
1072        sdev->pcmname = NULL;
1073        sdev->pcmid = -1;
1074        list_del(&ldev->list);
1075        layouts_list_items--;
1076        kfree(ldev);
1077 outnodev:
1078        of_node_put(sound);
1079        layout_device = NULL;
1080        return -ENODEV;
1081}
1082
1083static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
1084{
1085        struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
1086        int i;
1087
1088        for (i=0; i<MAX_CODECS_PER_BUS; i++) {
1089                if (ldev->codecs[i]) {
1090                        aoa_fabric_unlink_codec(ldev->codecs[i]);
1091                }
1092                ldev->codecs[i] = NULL;
1093        }
1094        list_del(&ldev->list);
1095        layouts_list_items--;
1096        of_node_put(ldev->sound);
1097
1098        ldev->gpio.methods->set_notify(&ldev->gpio,
1099                                       AOA_NOTIFY_HEADPHONE,
1100                                       NULL,
1101                                       NULL);
1102        ldev->gpio.methods->set_notify(&ldev->gpio,
1103                                       AOA_NOTIFY_LINE_OUT,
1104                                       NULL,
1105                                       NULL);
1106
1107        ldev->gpio.methods->exit(&ldev->gpio);
1108        layout_device = NULL;
1109        kfree(ldev);
1110        sdev->pcmid = -1;
1111        sdev->pcmname = NULL;
1112        return 0;
1113}
1114
1115#ifdef CONFIG_PM
1116static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t state)
1117{
1118        struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
1119
1120        if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
1121                ldev->gpio.methods->all_amps_off(&ldev->gpio);
1122
1123        return 0;
1124}
1125
1126static int aoa_fabric_layout_resume(struct soundbus_dev *sdev)
1127{
1128        struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
1129
1130        if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
1131                ldev->gpio.methods->all_amps_restore(&ldev->gpio);
1132
1133        return 0;
1134}
1135#endif
1136
1137static struct soundbus_driver aoa_soundbus_driver = {
1138        .name = "snd_aoa_soundbus_drv",
1139        .owner = THIS_MODULE,
1140        .probe = aoa_fabric_layout_probe,
1141        .remove = aoa_fabric_layout_remove,
1142#ifdef CONFIG_PM
1143        .suspend = aoa_fabric_layout_suspend,
1144        .resume = aoa_fabric_layout_resume,
1145#endif
1146        .driver = {
1147                .owner = THIS_MODULE,
1148        }
1149};
1150
1151static int __init aoa_fabric_layout_init(void)
1152{
1153        int err;
1154
1155        err = soundbus_register_driver(&aoa_soundbus_driver);
1156        if (err)
1157                return err;
1158        return 0;
1159}
1160
1161static void __exit aoa_fabric_layout_exit(void)
1162{
1163        soundbus_unregister_driver(&aoa_soundbus_driver);
1164        aoa_fabric_unregister(&layout_fabric);
1165}
1166
1167module_init(aoa_fabric_layout_init);
1168module_exit(aoa_fabric_layout_exit);
1169