root/sound/aoa/fabrics/layout.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. find_layout_by_id
  2. find_layout_by_device
  3. use_layout
  4. detect_choice_get
  5. detect_choice_put
  6. detected_get
  7. check_codec
  8. layout_found_codec
  9. layout_remove_codec
  10. layout_notify
  11. layout_attached_codec
  12. aoa_fabric_layout_probe
  13. aoa_fabric_layout_remove
  14. aoa_fabric_layout_suspend
  15. aoa_fabric_layout_resume
  16. aoa_fabric_layout_init
  17. aoa_fabric_layout_exit

   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 
  17 MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
  18 MODULE_LICENSE("GPL");
  19 MODULE_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 
  45 struct 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 
  55 struct codec_connect_info {
  56         char *name;
  57         struct codec_connection *connections;
  58 };
  59 
  60 #define LAYOUT_FLAG_COMBO_LINEOUT_SPDIF (1<<0)
  61 
  62 struct 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 
  77 MODULE_ALIAS("sound-layout-36");
  78 MODULE_ALIAS("sound-layout-41");
  79 MODULE_ALIAS("sound-layout-45");
  80 MODULE_ALIAS("sound-layout-47");
  81 MODULE_ALIAS("sound-layout-48");
  82 MODULE_ALIAS("sound-layout-49");
  83 MODULE_ALIAS("sound-layout-50");
  84 MODULE_ALIAS("sound-layout-51");
  85 MODULE_ALIAS("sound-layout-56");
  86 MODULE_ALIAS("sound-layout-57");
  87 MODULE_ALIAS("sound-layout-58");
  88 MODULE_ALIAS("sound-layout-60");
  89 MODULE_ALIAS("sound-layout-61");
  90 MODULE_ALIAS("sound-layout-62");
  91 MODULE_ALIAS("sound-layout-64");
  92 MODULE_ALIAS("sound-layout-65");
  93 MODULE_ALIAS("sound-layout-66");
  94 MODULE_ALIAS("sound-layout-67");
  95 MODULE_ALIAS("sound-layout-68");
  96 MODULE_ALIAS("sound-layout-69");
  97 MODULE_ALIAS("sound-layout-70");
  98 MODULE_ALIAS("sound-layout-72");
  99 MODULE_ALIAS("sound-layout-76");
 100 MODULE_ALIAS("sound-layout-80");
 101 MODULE_ALIAS("sound-layout-82");
 102 MODULE_ALIAS("sound-layout-84");
 103 MODULE_ALIAS("sound-layout-86");
 104 MODULE_ALIAS("sound-layout-90");
 105 MODULE_ALIAS("sound-layout-92");
 106 MODULE_ALIAS("sound-layout-94");
 107 MODULE_ALIAS("sound-layout-96");
 108 MODULE_ALIAS("sound-layout-98");
 109 MODULE_ALIAS("sound-layout-100");
 110 
 111 MODULE_ALIAS("aoa-device-id-14");
 112 MODULE_ALIAS("aoa-device-id-22");
 113 MODULE_ALIAS("aoa-device-id-31");
 114 MODULE_ALIAS("aoa-device-id-35");
 115 MODULE_ALIAS("aoa-device-id-44");
 116 
 117 /* onyx with all but microphone connected */
 118 static 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 */
 135 static 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 */
 159 static 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 */
 176 static 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 */
 193 static 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 */
 206 static 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 */
 219 static 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 
 235 static 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 
 243 static struct codec_connection topaz_input[] = {
 244         {
 245                 .connected = CC_DIGITALIN,
 246                 .codec_bit = 0,
 247         },
 248         {} /* terminate array by .connected == 0 */
 249 };
 250 
 251 static struct codec_connection topaz_output[] = {
 252         {
 253                 .connected = CC_DIGITALOUT,
 254                 .codec_bit = 1,
 255         },
 256         {} /* terminate array by .connected == 0 */
 257 };
 258 
 259 static 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 
 271 static 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 
 562 static 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 
 575 static 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 
 588 static 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 
 600 struct layout_dev;
 601 
 602 struct layout_dev_ptr {
 603         struct layout_dev *ptr;
 604 };
 605 
 606 struct 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 
 631 static LIST_HEAD(layouts_list);
 632 static 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... */
 635 static struct layout_dev *layout_device;
 636 
 637 #define control_info    snd_ctl_boolean_mono_info
 638 
 639 #define AMP_CONTROL(n, description)                                     \
 640 static 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 }                                                                       \
 649 static 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 }                                                                       \
 658 static 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 
 667 AMP_CONTROL(headphone, "Headphone Switch");
 668 AMP_CONTROL(speakers, "Speakers Switch");
 669 AMP_CONTROL(lineout, "Line-Out Switch");
 670 AMP_CONTROL(master, "Master Switch");
 671 
 672 static 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 
 690 static 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 
 708 static 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 
 718 static 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 
 728 static 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 
 750 static 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 
 759 static 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 
 768 static 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 
 818 static 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 
 838 static 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 
 850 static 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 
 895 static 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 
 988 static 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 
 996 static 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 
1097 static 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
1130 static 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 
1140 static 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 
1150 static SIMPLE_DEV_PM_OPS(aoa_fabric_layout_pm_ops,
1151         aoa_fabric_layout_suspend, aoa_fabric_layout_resume);
1152 
1153 #endif
1154 
1155 static 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 
1168 static int __init aoa_fabric_layout_init(void)
1169 {
1170         return soundbus_register_driver(&aoa_soundbus_driver);
1171 }
1172 
1173 static void __exit aoa_fabric_layout_exit(void)
1174 {
1175         soundbus_unregister_driver(&aoa_soundbus_driver);
1176         aoa_fabric_unregister(&layout_fabric);
1177 }
1178 
1179 module_init(aoa_fabric_layout_init);
1180 module_exit(aoa_fabric_layout_exit);

/* [<][>][^][v][top][bottom][index][help] */