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