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