linux/sound/isa/cs423x/cs4236_lib.c
<<
>>
Prefs
   1/*
   2 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
   3 *  Routines for control of CS4235/4236B/4237B/4238B/4239 chips
   4 *
   5 *  Note:
   6 *     -----
   7 *
   8 *  Bugs:
   9 *     -----
  10 *
  11 *   This program is free software; you can redistribute it and/or modify
  12 *   it under the terms of the GNU General Public License as published by
  13 *   the Free Software Foundation; either version 2 of the License, or
  14 *   (at your option) any later version.
  15 *
  16 *   This program is distributed in the hope that it will be useful,
  17 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  18 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19 *   GNU General Public License for more details.
  20 *
  21 *   You should have received a copy of the GNU General Public License
  22 *   along with this program; if not, write to the Free Software
  23 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  24 *
  25 */
  26
  27/*
  28 *  Indirect control registers (CS4236B+)
  29 * 
  30 *  C0
  31 *     D8: WSS reset (all chips)
  32 *
  33 *  C1 (all chips except CS4236)
  34 *     D7-D5: version 
  35 *     D4-D0: chip id
  36 *             11101 - CS4235
  37 *             01011 - CS4236B
  38 *             01000 - CS4237B
  39 *             01001 - CS4238B
  40 *             11110 - CS4239
  41 *
  42 *  C2
  43 *     D7-D4: 3D Space (CS4235,CS4237B,CS4238B,CS4239)
  44 *     D3-D0: 3D Center (CS4237B); 3D Volume (CS4238B)
  45 * 
  46 *  C3
  47 *     D7: 3D Enable (CS4237B)
  48 *     D6: 3D Mono Enable (CS4237B)
  49 *     D5: 3D Serial Output (CS4237B,CS4238B)
  50 *     D4: 3D Enable (CS4235,CS4238B,CS4239)
  51 *
  52 *  C4
  53 *     D7: consumer serial port enable (CS4237B,CS4238B)
  54 *     D6: channels status block reset (CS4237B,CS4238B)
  55 *     D5: user bit in sub-frame of digital audio data (CS4237B,CS4238B)
  56 *     D4: validity bit bit in sub-frame of digital audio data (CS4237B,CS4238B)
  57 * 
  58 *  C5  lower channel status (digital serial data description) (CS4237B,CS4238B)
  59 *     D7-D6: first two bits of category code
  60 *     D5: lock
  61 *     D4-D3: pre-emphasis (0 = none, 1 = 50/15us)
  62 *     D2: copy/copyright (0 = copy inhibited)
  63 *     D1: 0 = digital audio / 1 = non-digital audio
  64 *     
  65 *  C6  upper channel status (digital serial data description) (CS4237B,CS4238B)
  66 *     D7-D6: sample frequency (0 = 44.1kHz)
  67 *     D5: generation status (0 = no indication, 1 = original/commercially precaptureed data)
  68 *     D4-D0: category code (upper bits)
  69 *
  70 *  C7  reserved (must write 0)
  71 *
  72 *  C8  wavetable control
  73 *     D7: volume control interrupt enable (CS4235,CS4239)
  74 *     D6: hardware volume control format (CS4235,CS4239)
  75 *     D3: wavetable serial port enable (all chips)
  76 *     D2: DSP serial port switch (all chips)
  77 *     D1: disable MCLK (all chips)
  78 *     D0: force BRESET low (all chips)
  79 *
  80 */
  81
  82#include <asm/io.h>
  83#include <linux/delay.h>
  84#include <linux/init.h>
  85#include <linux/time.h>
  86#include <linux/wait.h>
  87#include <sound/core.h>
  88#include <sound/wss.h>
  89#include <sound/asoundef.h>
  90
  91/*
  92 *
  93 */
  94
  95static unsigned char snd_cs4236_ext_map[18] = {
  96        /* CS4236_LEFT_LINE */          0xff,
  97        /* CS4236_RIGHT_LINE */         0xff,
  98        /* CS4236_LEFT_MIC */           0xdf,
  99        /* CS4236_RIGHT_MIC */          0xdf,
 100        /* CS4236_LEFT_MIX_CTRL */      0xe0 | 0x18,
 101        /* CS4236_RIGHT_MIX_CTRL */     0xe0,
 102        /* CS4236_LEFT_FM */            0xbf,
 103        /* CS4236_RIGHT_FM */           0xbf,
 104        /* CS4236_LEFT_DSP */           0xbf,
 105        /* CS4236_RIGHT_DSP */          0xbf,
 106        /* CS4236_RIGHT_LOOPBACK */     0xbf,
 107        /* CS4236_DAC_MUTE */           0xe0,
 108        /* CS4236_ADC_RATE */           0x01,   /* 48kHz */
 109        /* CS4236_DAC_RATE */           0x01,   /* 48kHz */
 110        /* CS4236_LEFT_MASTER */        0xbf,
 111        /* CS4236_RIGHT_MASTER */       0xbf,
 112        /* CS4236_LEFT_WAVE */          0xbf,
 113        /* CS4236_RIGHT_WAVE */         0xbf
 114};
 115
 116/*
 117 *
 118 */
 119
 120static void snd_cs4236_ctrl_out(struct snd_wss *chip,
 121                                unsigned char reg, unsigned char val)
 122{
 123        outb(reg, chip->cport + 3);
 124        outb(chip->cimage[reg] = val, chip->cport + 4);
 125}
 126
 127static unsigned char snd_cs4236_ctrl_in(struct snd_wss *chip, unsigned char reg)
 128{
 129        outb(reg, chip->cport + 3);
 130        return inb(chip->cport + 4);
 131}
 132
 133/*
 134 *  PCM
 135 */
 136
 137#define CLOCKS 8
 138
 139static struct snd_ratnum clocks[CLOCKS] = {
 140        { .num = 16934400, .den_min = 353, .den_max = 353, .den_step = 1 },
 141        { .num = 16934400, .den_min = 529, .den_max = 529, .den_step = 1 },
 142        { .num = 16934400, .den_min = 617, .den_max = 617, .den_step = 1 },
 143        { .num = 16934400, .den_min = 1058, .den_max = 1058, .den_step = 1 },
 144        { .num = 16934400, .den_min = 1764, .den_max = 1764, .den_step = 1 },
 145        { .num = 16934400, .den_min = 2117, .den_max = 2117, .den_step = 1 },
 146        { .num = 16934400, .den_min = 2558, .den_max = 2558, .den_step = 1 },
 147        { .num = 16934400/16, .den_min = 21, .den_max = 192, .den_step = 1 }
 148};
 149
 150static struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
 151        .nrats = CLOCKS,
 152        .rats = clocks,
 153};
 154
 155static int snd_cs4236_xrate(struct snd_pcm_runtime *runtime)
 156{
 157        return snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 158                                             &hw_constraints_clocks);
 159}
 160
 161static unsigned char divisor_to_rate_register(unsigned int divisor)
 162{
 163        switch (divisor) {
 164        case 353:       return 1;
 165        case 529:       return 2;
 166        case 617:       return 3;
 167        case 1058:      return 4;
 168        case 1764:      return 5;
 169        case 2117:      return 6;
 170        case 2558:      return 7;
 171        default:
 172                if (divisor < 21 || divisor > 192) {
 173                        snd_BUG();
 174                        return 192;
 175                }
 176                return divisor;
 177        }
 178}
 179
 180static void snd_cs4236_playback_format(struct snd_wss *chip,
 181                                       struct snd_pcm_hw_params *params,
 182                                       unsigned char pdfr)
 183{
 184        unsigned long flags;
 185        unsigned char rate = divisor_to_rate_register(params->rate_den);
 186        
 187        spin_lock_irqsave(&chip->reg_lock, flags);
 188        /* set fast playback format change and clean playback FIFO */
 189        snd_wss_out(chip, CS4231_ALT_FEATURE_1,
 190                    chip->image[CS4231_ALT_FEATURE_1] | 0x10);
 191        snd_wss_out(chip, CS4231_PLAYBK_FORMAT, pdfr & 0xf0);
 192        snd_wss_out(chip, CS4231_ALT_FEATURE_1,
 193                    chip->image[CS4231_ALT_FEATURE_1] & ~0x10);
 194        snd_cs4236_ext_out(chip, CS4236_DAC_RATE, rate);
 195        spin_unlock_irqrestore(&chip->reg_lock, flags);
 196}
 197
 198static void snd_cs4236_capture_format(struct snd_wss *chip,
 199                                      struct snd_pcm_hw_params *params,
 200                                      unsigned char cdfr)
 201{
 202        unsigned long flags;
 203        unsigned char rate = divisor_to_rate_register(params->rate_den);
 204        
 205        spin_lock_irqsave(&chip->reg_lock, flags);
 206        /* set fast capture format change and clean capture FIFO */
 207        snd_wss_out(chip, CS4231_ALT_FEATURE_1,
 208                    chip->image[CS4231_ALT_FEATURE_1] | 0x20);
 209        snd_wss_out(chip, CS4231_REC_FORMAT, cdfr & 0xf0);
 210        snd_wss_out(chip, CS4231_ALT_FEATURE_1,
 211                    chip->image[CS4231_ALT_FEATURE_1] & ~0x20);
 212        snd_cs4236_ext_out(chip, CS4236_ADC_RATE, rate);
 213        spin_unlock_irqrestore(&chip->reg_lock, flags);
 214}
 215
 216#ifdef CONFIG_PM
 217
 218static void snd_cs4236_suspend(struct snd_wss *chip)
 219{
 220        int reg;
 221        unsigned long flags;
 222        
 223        spin_lock_irqsave(&chip->reg_lock, flags);
 224        for (reg = 0; reg < 32; reg++)
 225                chip->image[reg] = snd_wss_in(chip, reg);
 226        for (reg = 0; reg < 18; reg++)
 227                chip->eimage[reg] = snd_cs4236_ext_in(chip, CS4236_I23VAL(reg));
 228        for (reg = 2; reg < 9; reg++)
 229                chip->cimage[reg] = snd_cs4236_ctrl_in(chip, reg);
 230        spin_unlock_irqrestore(&chip->reg_lock, flags);
 231}
 232
 233static void snd_cs4236_resume(struct snd_wss *chip)
 234{
 235        int reg;
 236        unsigned long flags;
 237        
 238        snd_wss_mce_up(chip);
 239        spin_lock_irqsave(&chip->reg_lock, flags);
 240        for (reg = 0; reg < 32; reg++) {
 241                switch (reg) {
 242                case CS4236_EXT_REG:
 243                case CS4231_VERSION:
 244                case 27:        /* why? CS4235 - master left */
 245                case 29:        /* why? CS4235 - master right */
 246                        break;
 247                default:
 248                        snd_wss_out(chip, reg, chip->image[reg]);
 249                        break;
 250                }
 251        }
 252        for (reg = 0; reg < 18; reg++)
 253                snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), chip->eimage[reg]);
 254        for (reg = 2; reg < 9; reg++) {
 255                switch (reg) {
 256                case 7:
 257                        break;
 258                default:
 259                        snd_cs4236_ctrl_out(chip, reg, chip->cimage[reg]);
 260                }
 261        }
 262        spin_unlock_irqrestore(&chip->reg_lock, flags);
 263        snd_wss_mce_down(chip);
 264}
 265
 266#endif /* CONFIG_PM */
 267
 268int snd_cs4236_create(struct snd_card *card,
 269                      unsigned long port,
 270                      unsigned long cport,
 271                      int irq, int dma1, int dma2,
 272                      unsigned short hardware,
 273                      unsigned short hwshare,
 274                      struct snd_wss **rchip)
 275{
 276        struct snd_wss *chip;
 277        unsigned char ver1, ver2;
 278        unsigned int reg;
 279        int err;
 280
 281        *rchip = NULL;
 282        if (hardware == WSS_HW_DETECT)
 283                hardware = WSS_HW_DETECT3;
 284        if (cport < 0x100) {
 285                snd_printk(KERN_ERR "please, specify control port "
 286                           "for CS4236+ chips\n");
 287                return -ENODEV;
 288        }
 289        err = snd_wss_create(card, port, cport,
 290                             irq, dma1, dma2, hardware, hwshare, &chip);
 291        if (err < 0)
 292                return err;
 293
 294        if (!(chip->hardware & WSS_HW_CS4236B_MASK)) {
 295                snd_printk(KERN_ERR "CS4236+: MODE3 and extended registers "
 296                           "not available, hardware=0x%x\n", chip->hardware);
 297                snd_device_free(card, chip);
 298                return -ENODEV;
 299        }
 300#if 0
 301        {
 302                int idx;
 303                for (idx = 0; idx < 8; idx++)
 304                        snd_printk(KERN_DEBUG "CD%i = 0x%x\n",
 305                                   idx, inb(chip->cport + idx));
 306                for (idx = 0; idx < 9; idx++)
 307                        snd_printk(KERN_DEBUG "C%i = 0x%x\n",
 308                                   idx, snd_cs4236_ctrl_in(chip, idx));
 309        }
 310#endif
 311        ver1 = snd_cs4236_ctrl_in(chip, 1);
 312        ver2 = snd_cs4236_ext_in(chip, CS4236_VERSION);
 313        snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n", cport, ver1, ver2);
 314        if (ver1 != ver2) {
 315                snd_printk(KERN_ERR "CS4236+ chip detected, but "
 316                           "control port 0x%lx is not valid\n", cport);
 317                snd_device_free(card, chip);
 318                return -ENODEV;
 319        }
 320        snd_cs4236_ctrl_out(chip, 0, 0x00);
 321        snd_cs4236_ctrl_out(chip, 2, 0xff);
 322        snd_cs4236_ctrl_out(chip, 3, 0x00);
 323        snd_cs4236_ctrl_out(chip, 4, 0x80);
 324        snd_cs4236_ctrl_out(chip, 5, ((IEC958_AES1_CON_PCM_CODER & 3) << 6) | IEC958_AES0_CON_EMPHASIS_NONE);
 325        snd_cs4236_ctrl_out(chip, 6, IEC958_AES1_CON_PCM_CODER >> 2);
 326        snd_cs4236_ctrl_out(chip, 7, 0x00);
 327        /* 0x8c for C8 is valid for Turtle Beach Malibu - the IEC-958 output */
 328        /* is working with this setup, other hardware should have */
 329        /* different signal paths and this value should be selectable */
 330        /* in the future */
 331        snd_cs4236_ctrl_out(chip, 8, 0x8c);
 332        chip->rate_constraint = snd_cs4236_xrate;
 333        chip->set_playback_format = snd_cs4236_playback_format;
 334        chip->set_capture_format = snd_cs4236_capture_format;
 335#ifdef CONFIG_PM
 336        chip->suspend = snd_cs4236_suspend;
 337        chip->resume = snd_cs4236_resume;
 338#endif
 339
 340        /* initialize extended registers */
 341        for (reg = 0; reg < sizeof(snd_cs4236_ext_map); reg++)
 342                snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), snd_cs4236_ext_map[reg]);
 343
 344        /* initialize compatible but more featured registers */
 345        snd_wss_out(chip, CS4231_LEFT_INPUT, 0x40);
 346        snd_wss_out(chip, CS4231_RIGHT_INPUT, 0x40);
 347        snd_wss_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff);
 348        snd_wss_out(chip, CS4231_AUX1_RIGHT_INPUT, 0xff);
 349        snd_wss_out(chip, CS4231_AUX2_LEFT_INPUT, 0xdf);
 350        snd_wss_out(chip, CS4231_AUX2_RIGHT_INPUT, 0xdf);
 351        snd_wss_out(chip, CS4231_RIGHT_LINE_IN, 0xff);
 352        snd_wss_out(chip, CS4231_LEFT_LINE_IN, 0xff);
 353        snd_wss_out(chip, CS4231_RIGHT_LINE_IN, 0xff);
 354        switch (chip->hardware) {
 355        case WSS_HW_CS4235:
 356        case WSS_HW_CS4239:
 357                snd_wss_out(chip, CS4235_LEFT_MASTER, 0xff);
 358                snd_wss_out(chip, CS4235_RIGHT_MASTER, 0xff);
 359                break;
 360        }
 361
 362        *rchip = chip;
 363        return 0;
 364}
 365
 366int snd_cs4236_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm)
 367{
 368        struct snd_pcm *pcm;
 369        int err;
 370        
 371        err = snd_wss_pcm(chip, device, &pcm);
 372        if (err < 0)
 373                return err;
 374        pcm->info_flags &= ~SNDRV_PCM_INFO_JOINT_DUPLEX;
 375        if (rpcm)
 376                *rpcm = pcm;
 377        return 0;
 378}
 379
 380/*
 381 *  MIXER
 382 */
 383
 384#define CS4236_SINGLE(xname, xindex, reg, shift, mask, invert) \
 385{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
 386  .info = snd_cs4236_info_single, \
 387  .get = snd_cs4236_get_single, .put = snd_cs4236_put_single, \
 388  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
 389
 390static int snd_cs4236_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 391{
 392        int mask = (kcontrol->private_value >> 16) & 0xff;
 393
 394        uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
 395        uinfo->count = 1;
 396        uinfo->value.integer.min = 0;
 397        uinfo->value.integer.max = mask;
 398        return 0;
 399}
 400
 401static int snd_cs4236_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 402{
 403        struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 404        unsigned long flags;
 405        int reg = kcontrol->private_value & 0xff;
 406        int shift = (kcontrol->private_value >> 8) & 0xff;
 407        int mask = (kcontrol->private_value >> 16) & 0xff;
 408        int invert = (kcontrol->private_value >> 24) & 0xff;
 409        
 410        spin_lock_irqsave(&chip->reg_lock, flags);
 411        ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(reg)] >> shift) & mask;
 412        spin_unlock_irqrestore(&chip->reg_lock, flags);
 413        if (invert)
 414                ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
 415        return 0;
 416}
 417
 418static int snd_cs4236_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 419{
 420        struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 421        unsigned long flags;
 422        int reg = kcontrol->private_value & 0xff;
 423        int shift = (kcontrol->private_value >> 8) & 0xff;
 424        int mask = (kcontrol->private_value >> 16) & 0xff;
 425        int invert = (kcontrol->private_value >> 24) & 0xff;
 426        int change;
 427        unsigned short val;
 428        
 429        val = (ucontrol->value.integer.value[0] & mask);
 430        if (invert)
 431                val = mask - val;
 432        val <<= shift;
 433        spin_lock_irqsave(&chip->reg_lock, flags);
 434        val = (chip->eimage[CS4236_REG(reg)] & ~(mask << shift)) | val;
 435        change = val != chip->eimage[CS4236_REG(reg)];
 436        snd_cs4236_ext_out(chip, reg, val);
 437        spin_unlock_irqrestore(&chip->reg_lock, flags);
 438        return change;
 439}
 440
 441#define CS4236_SINGLEC(xname, xindex, reg, shift, mask, invert) \
 442{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
 443  .info = snd_cs4236_info_single, \
 444  .get = snd_cs4236_get_singlec, .put = snd_cs4236_put_singlec, \
 445  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
 446
 447static int snd_cs4236_get_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 448{
 449        struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 450        unsigned long flags;
 451        int reg = kcontrol->private_value & 0xff;
 452        int shift = (kcontrol->private_value >> 8) & 0xff;
 453        int mask = (kcontrol->private_value >> 16) & 0xff;
 454        int invert = (kcontrol->private_value >> 24) & 0xff;
 455        
 456        spin_lock_irqsave(&chip->reg_lock, flags);
 457        ucontrol->value.integer.value[0] = (chip->cimage[reg] >> shift) & mask;
 458        spin_unlock_irqrestore(&chip->reg_lock, flags);
 459        if (invert)
 460                ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
 461        return 0;
 462}
 463
 464static int snd_cs4236_put_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 465{
 466        struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 467        unsigned long flags;
 468        int reg = kcontrol->private_value & 0xff;
 469        int shift = (kcontrol->private_value >> 8) & 0xff;
 470        int mask = (kcontrol->private_value >> 16) & 0xff;
 471        int invert = (kcontrol->private_value >> 24) & 0xff;
 472        int change;
 473        unsigned short val;
 474        
 475        val = (ucontrol->value.integer.value[0] & mask);
 476        if (invert)
 477                val = mask - val;
 478        val <<= shift;
 479        spin_lock_irqsave(&chip->reg_lock, flags);
 480        val = (chip->cimage[reg] & ~(mask << shift)) | val;
 481        change = val != chip->cimage[reg];
 482        snd_cs4236_ctrl_out(chip, reg, val);
 483        spin_unlock_irqrestore(&chip->reg_lock, flags);
 484        return change;
 485}
 486
 487#define CS4236_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
 488{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
 489  .info = snd_cs4236_info_double, \
 490  .get = snd_cs4236_get_double, .put = snd_cs4236_put_double, \
 491  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
 492
 493static int snd_cs4236_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 494{
 495        int mask = (kcontrol->private_value >> 24) & 0xff;
 496
 497        uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
 498        uinfo->count = 2;
 499        uinfo->value.integer.min = 0;
 500        uinfo->value.integer.max = mask;
 501        return 0;
 502}
 503
 504static int snd_cs4236_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 505{
 506        struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 507        unsigned long flags;
 508        int left_reg = kcontrol->private_value & 0xff;
 509        int right_reg = (kcontrol->private_value >> 8) & 0xff;
 510        int shift_left = (kcontrol->private_value >> 16) & 0x07;
 511        int shift_right = (kcontrol->private_value >> 19) & 0x07;
 512        int mask = (kcontrol->private_value >> 24) & 0xff;
 513        int invert = (kcontrol->private_value >> 22) & 1;
 514        
 515        spin_lock_irqsave(&chip->reg_lock, flags);
 516        ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(left_reg)] >> shift_left) & mask;
 517        ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask;
 518        spin_unlock_irqrestore(&chip->reg_lock, flags);
 519        if (invert) {
 520                ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
 521                ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
 522        }
 523        return 0;
 524}
 525
 526static int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 527{
 528        struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 529        unsigned long flags;
 530        int left_reg = kcontrol->private_value & 0xff;
 531        int right_reg = (kcontrol->private_value >> 8) & 0xff;
 532        int shift_left = (kcontrol->private_value >> 16) & 0x07;
 533        int shift_right = (kcontrol->private_value >> 19) & 0x07;
 534        int mask = (kcontrol->private_value >> 24) & 0xff;
 535        int invert = (kcontrol->private_value >> 22) & 1;
 536        int change;
 537        unsigned short val1, val2;
 538        
 539        val1 = ucontrol->value.integer.value[0] & mask;
 540        val2 = ucontrol->value.integer.value[1] & mask;
 541        if (invert) {
 542                val1 = mask - val1;
 543                val2 = mask - val2;
 544        }
 545        val1 <<= shift_left;
 546        val2 <<= shift_right;
 547        spin_lock_irqsave(&chip->reg_lock, flags);
 548        if (left_reg != right_reg) {
 549                val1 = (chip->eimage[CS4236_REG(left_reg)] & ~(mask << shift_left)) | val1;
 550                val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2;
 551                change = val1 != chip->eimage[CS4236_REG(left_reg)] || val2 != chip->eimage[CS4236_REG(right_reg)];
 552                snd_cs4236_ext_out(chip, left_reg, val1);
 553                snd_cs4236_ext_out(chip, right_reg, val2);
 554        } else {
 555                val1 = (chip->eimage[CS4236_REG(left_reg)] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
 556                change = val1 != chip->eimage[CS4236_REG(left_reg)];
 557                snd_cs4236_ext_out(chip, left_reg, val1);
 558        }
 559        spin_unlock_irqrestore(&chip->reg_lock, flags);
 560        return change;
 561}
 562
 563#define CS4236_DOUBLE1(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
 564{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
 565  .info = snd_cs4236_info_double, \
 566  .get = snd_cs4236_get_double1, .put = snd_cs4236_put_double1, \
 567  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
 568
 569static int snd_cs4236_get_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 570{
 571        struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 572        unsigned long flags;
 573        int left_reg = kcontrol->private_value & 0xff;
 574        int right_reg = (kcontrol->private_value >> 8) & 0xff;
 575        int shift_left = (kcontrol->private_value >> 16) & 0x07;
 576        int shift_right = (kcontrol->private_value >> 19) & 0x07;
 577        int mask = (kcontrol->private_value >> 24) & 0xff;
 578        int invert = (kcontrol->private_value >> 22) & 1;
 579        
 580        spin_lock_irqsave(&chip->reg_lock, flags);
 581        ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
 582        ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask;
 583        spin_unlock_irqrestore(&chip->reg_lock, flags);
 584        if (invert) {
 585                ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
 586                ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
 587        }
 588        return 0;
 589}
 590
 591static int snd_cs4236_put_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 592{
 593        struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 594        unsigned long flags;
 595        int left_reg = kcontrol->private_value & 0xff;
 596        int right_reg = (kcontrol->private_value >> 8) & 0xff;
 597        int shift_left = (kcontrol->private_value >> 16) & 0x07;
 598        int shift_right = (kcontrol->private_value >> 19) & 0x07;
 599        int mask = (kcontrol->private_value >> 24) & 0xff;
 600        int invert = (kcontrol->private_value >> 22) & 1;
 601        int change;
 602        unsigned short val1, val2;
 603        
 604        val1 = ucontrol->value.integer.value[0] & mask;
 605        val2 = ucontrol->value.integer.value[1] & mask;
 606        if (invert) {
 607                val1 = mask - val1;
 608                val2 = mask - val2;
 609        }
 610        val1 <<= shift_left;
 611        val2 <<= shift_right;
 612        spin_lock_irqsave(&chip->reg_lock, flags);
 613        val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
 614        val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2;
 615        change = val1 != chip->image[left_reg] || val2 != chip->eimage[CS4236_REG(right_reg)];
 616        snd_wss_out(chip, left_reg, val1);
 617        snd_cs4236_ext_out(chip, right_reg, val2);
 618        spin_unlock_irqrestore(&chip->reg_lock, flags);
 619        return change;
 620}
 621
 622#define CS4236_MASTER_DIGITAL(xname, xindex) \
 623{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
 624  .info = snd_cs4236_info_double, \
 625  .get = snd_cs4236_get_master_digital, .put = snd_cs4236_put_master_digital, \
 626  .private_value = 71 << 24 }
 627
 628static inline int snd_cs4236_mixer_master_digital_invert_volume(int vol)
 629{
 630        return (vol < 64) ? 63 - vol : 64 + (71 - vol);
 631}        
 632
 633static int snd_cs4236_get_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 634{
 635        struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 636        unsigned long flags;
 637        
 638        spin_lock_irqsave(&chip->reg_lock, flags);
 639        ucontrol->value.integer.value[0] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & 0x7f);
 640        ucontrol->value.integer.value[1] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & 0x7f);
 641        spin_unlock_irqrestore(&chip->reg_lock, flags);
 642        return 0;
 643}
 644
 645static int snd_cs4236_put_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 646{
 647        struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 648        unsigned long flags;
 649        int change;
 650        unsigned short val1, val2;
 651        
 652        val1 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[0] & 0x7f);
 653        val2 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[1] & 0x7f);
 654        spin_lock_irqsave(&chip->reg_lock, flags);
 655        val1 = (chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & ~0x7f) | val1;
 656        val2 = (chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & ~0x7f) | val2;
 657        change = val1 != chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] || val2 != chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)];
 658        snd_cs4236_ext_out(chip, CS4236_LEFT_MASTER, val1);
 659        snd_cs4236_ext_out(chip, CS4236_RIGHT_MASTER, val2);
 660        spin_unlock_irqrestore(&chip->reg_lock, flags);
 661        return change;
 662}
 663
 664#define CS4235_OUTPUT_ACCU(xname, xindex) \
 665{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
 666  .info = snd_cs4236_info_double, \
 667  .get = snd_cs4235_get_output_accu, .put = snd_cs4235_put_output_accu, \
 668  .private_value = 3 << 24 }
 669
 670static inline int snd_cs4235_mixer_output_accu_get_volume(int vol)
 671{
 672        switch ((vol >> 5) & 3) {
 673        case 0: return 1;
 674        case 1: return 3;
 675        case 2: return 2;
 676        case 3: return 0;
 677        }
 678        return 3;
 679}
 680
 681static inline int snd_cs4235_mixer_output_accu_set_volume(int vol)
 682{
 683        switch (vol & 3) {
 684        case 0: return 3 << 5;
 685        case 1: return 0 << 5;
 686        case 2: return 2 << 5;
 687        case 3: return 1 << 5;
 688        }
 689        return 1 << 5;
 690}
 691
 692static int snd_cs4235_get_output_accu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 693{
 694        struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 695        unsigned long flags;
 696        
 697        spin_lock_irqsave(&chip->reg_lock, flags);
 698        ucontrol->value.integer.value[0] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_LEFT_MASTER]);
 699        ucontrol->value.integer.value[1] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_RIGHT_MASTER]);
 700        spin_unlock_irqrestore(&chip->reg_lock, flags);
 701        return 0;
 702}
 703
 704static int snd_cs4235_put_output_accu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 705{
 706        struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 707        unsigned long flags;
 708        int change;
 709        unsigned short val1, val2;
 710        
 711        val1 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[0]);
 712        val2 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[1]);
 713        spin_lock_irqsave(&chip->reg_lock, flags);
 714        val1 = (chip->image[CS4235_LEFT_MASTER] & ~(3 << 5)) | val1;
 715        val2 = (chip->image[CS4235_RIGHT_MASTER] & ~(3 << 5)) | val2;
 716        change = val1 != chip->image[CS4235_LEFT_MASTER] || val2 != chip->image[CS4235_RIGHT_MASTER];
 717        snd_wss_out(chip, CS4235_LEFT_MASTER, val1);
 718        snd_wss_out(chip, CS4235_RIGHT_MASTER, val2);
 719        spin_unlock_irqrestore(&chip->reg_lock, flags);
 720        return change;
 721}
 722
 723static struct snd_kcontrol_new snd_cs4236_controls[] = {
 724
 725CS4236_DOUBLE("Master Digital Playback Switch", 0,
 726                CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
 727CS4236_DOUBLE("Master Digital Capture Switch", 0,
 728                CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
 729CS4236_MASTER_DIGITAL("Master Digital Volume", 0),
 730
 731CS4236_DOUBLE("Capture Boost Volume", 0,
 732                CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
 733
 734WSS_DOUBLE("PCM Playback Switch", 0,
 735                CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
 736WSS_DOUBLE("PCM Playback Volume", 0,
 737                CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
 738
 739CS4236_DOUBLE("DSP Playback Switch", 0,
 740                CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
 741CS4236_DOUBLE("DSP Playback Volume", 0,
 742                CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1),
 743
 744CS4236_DOUBLE("FM Playback Switch", 0,
 745                CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1),
 746CS4236_DOUBLE("FM Playback Volume", 0,
 747                CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1),
 748
 749CS4236_DOUBLE("Wavetable Playback Switch", 0,
 750                CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1),
 751CS4236_DOUBLE("Wavetable Playback Volume", 0,
 752                CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1),
 753
 754WSS_DOUBLE("Synth Playback Switch", 0,
 755                CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
 756WSS_DOUBLE("Synth Volume", 0,
 757                CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
 758WSS_DOUBLE("Synth Capture Switch", 0,
 759                CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
 760WSS_DOUBLE("Synth Capture Bypass", 0,
 761                CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 5, 5, 1, 1),
 762
 763CS4236_DOUBLE("Mic Playback Switch", 0,
 764                CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
 765CS4236_DOUBLE("Mic Capture Switch", 0,
 766                CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
 767CS4236_DOUBLE("Mic Volume", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 0, 0, 31, 1),
 768CS4236_DOUBLE("Mic Playback Boost", 0,
 769                CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0),
 770
 771WSS_DOUBLE("Line Playback Switch", 0,
 772                CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
 773WSS_DOUBLE("Line Volume", 0,
 774                CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
 775WSS_DOUBLE("Line Capture Switch", 0,
 776                CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
 777WSS_DOUBLE("Line Capture Bypass", 0,
 778                CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 5, 5, 1, 1),
 779
 780WSS_DOUBLE("CD Playback Switch", 0,
 781                CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
 782WSS_DOUBLE("CD Volume", 0,
 783                CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
 784WSS_DOUBLE("CD Capture Switch", 0,
 785                CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
 786
 787CS4236_DOUBLE1("Mono Output Playback Switch", 0,
 788                CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
 789CS4236_DOUBLE1("Mono Playback Switch", 0,
 790                CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
 791WSS_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
 792WSS_SINGLE("Mono Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0),
 793
 794WSS_DOUBLE("Capture Volume", 0,
 795                CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
 796WSS_DOUBLE("Analog Loopback Capture Switch", 0,
 797                CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
 798
 799WSS_SINGLE("Digital Loopback Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
 800CS4236_DOUBLE1("Digital Loopback Playback Volume", 0,
 801                CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1)
 802};
 803
 804static struct snd_kcontrol_new snd_cs4235_controls[] = {
 805
 806WSS_DOUBLE("Master Switch", 0,
 807                CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1),
 808WSS_DOUBLE("Master Volume", 0,
 809                CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1),
 810
 811CS4235_OUTPUT_ACCU("Playback Volume", 0),
 812
 813CS4236_DOUBLE("Master Digital Playback Switch", 0,
 814                CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
 815CS4236_DOUBLE("Master Digital Capture Switch", 0,
 816                CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),
 817CS4236_MASTER_DIGITAL("Master Digital Volume", 0),
 818
 819WSS_DOUBLE("Master Digital Playback Switch", 1,
 820                CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
 821WSS_DOUBLE("Master Digital Capture Switch", 1,
 822                CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1),
 823WSS_DOUBLE("Master Digital Volume", 1,
 824                CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
 825
 826CS4236_DOUBLE("Capture Volume", 0,
 827                CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1),
 828
 829WSS_DOUBLE("PCM Switch", 0,
 830                CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
 831WSS_DOUBLE("PCM Volume", 0,
 832                CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
 833
 834CS4236_DOUBLE("DSP Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),
 835
 836CS4236_DOUBLE("FM Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1),
 837
 838CS4236_DOUBLE("Wavetable Switch", 0,
 839                CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1),
 840
 841CS4236_DOUBLE("Mic Capture Switch", 0,
 842                CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),
 843CS4236_DOUBLE("Mic Playback Switch", 0,
 844                CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),
 845CS4236_SINGLE("Mic Volume", 0, CS4236_LEFT_MIC, 0, 31, 1),
 846CS4236_SINGLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, 5, 1, 0),
 847
 848WSS_DOUBLE("Aux Playback Switch", 0,
 849                CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
 850WSS_DOUBLE("Aux Capture Switch", 0,
 851                CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1),
 852WSS_DOUBLE("Aux Volume", 0,
 853                CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
 854
 855WSS_DOUBLE("Aux Playback Switch", 1,
 856                CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
 857WSS_DOUBLE("Aux Capture Switch", 1,
 858                CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1),
 859WSS_DOUBLE("Aux Volume", 1,
 860                CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
 861
 862CS4236_DOUBLE1("Master Mono Switch", 0,
 863                CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1),
 864
 865CS4236_DOUBLE1("Mono Switch", 0,
 866                CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1),
 867WSS_SINGLE("Mono Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
 868
 869WSS_DOUBLE("Analog Loopback Switch", 0,
 870                CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),
 871};
 872
 873#define CS4236_IEC958_ENABLE(xname, xindex) \
 874{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
 875  .info = snd_cs4236_info_single, \
 876  .get = snd_cs4236_get_iec958_switch, .put = snd_cs4236_put_iec958_switch, \
 877  .private_value = 1 << 16 }
 878
 879static int snd_cs4236_get_iec958_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 880{
 881        struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 882        unsigned long flags;
 883        
 884        spin_lock_irqsave(&chip->reg_lock, flags);
 885        ucontrol->value.integer.value[0] = chip->image[CS4231_ALT_FEATURE_1] & 0x02 ? 1 : 0;
 886#if 0
 887        printk(KERN_DEBUG "get valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, "
 888               "C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
 889                        snd_wss_in(chip, CS4231_ALT_FEATURE_1),
 890                        snd_cs4236_ctrl_in(chip, 3),
 891                        snd_cs4236_ctrl_in(chip, 4),
 892                        snd_cs4236_ctrl_in(chip, 5),
 893                        snd_cs4236_ctrl_in(chip, 6),
 894                        snd_cs4236_ctrl_in(chip, 8));
 895#endif
 896        spin_unlock_irqrestore(&chip->reg_lock, flags);
 897        return 0;
 898}
 899
 900static int snd_cs4236_put_iec958_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 901{
 902        struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
 903        unsigned long flags;
 904        int change;
 905        unsigned short enable, val;
 906        
 907        enable = ucontrol->value.integer.value[0] & 1;
 908
 909        mutex_lock(&chip->mce_mutex);
 910        snd_wss_mce_up(chip);
 911        spin_lock_irqsave(&chip->reg_lock, flags);
 912        val = (chip->image[CS4231_ALT_FEATURE_1] & ~0x0e) | (0<<2) | (enable << 1);
 913        change = val != chip->image[CS4231_ALT_FEATURE_1];
 914        snd_wss_out(chip, CS4231_ALT_FEATURE_1, val);
 915        val = snd_cs4236_ctrl_in(chip, 4) | 0xc0;
 916        snd_cs4236_ctrl_out(chip, 4, val);
 917        udelay(100);
 918        val &= ~0x40;
 919        snd_cs4236_ctrl_out(chip, 4, val);
 920        spin_unlock_irqrestore(&chip->reg_lock, flags);
 921        snd_wss_mce_down(chip);
 922        mutex_unlock(&chip->mce_mutex);
 923
 924#if 0
 925        printk(KERN_DEBUG "set valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, "
 926               "C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
 927                        snd_wss_in(chip, CS4231_ALT_FEATURE_1),
 928                        snd_cs4236_ctrl_in(chip, 3),
 929                        snd_cs4236_ctrl_in(chip, 4),
 930                        snd_cs4236_ctrl_in(chip, 5),
 931                        snd_cs4236_ctrl_in(chip, 6),
 932                        snd_cs4236_ctrl_in(chip, 8));
 933#endif
 934        return change;
 935}
 936
 937static struct snd_kcontrol_new snd_cs4236_iec958_controls[] = {
 938CS4236_IEC958_ENABLE("IEC958 Output Enable", 0),
 939CS4236_SINGLEC("IEC958 Output Validity", 0, 4, 4, 1, 0),
 940CS4236_SINGLEC("IEC958 Output User", 0, 4, 5, 1, 0),
 941CS4236_SINGLEC("IEC958 Output CSBR", 0, 4, 6, 1, 0),
 942CS4236_SINGLEC("IEC958 Output Channel Status Low", 0, 5, 1, 127, 0),
 943CS4236_SINGLEC("IEC958 Output Channel Status High", 0, 6, 0, 255, 0)
 944};
 945
 946static struct snd_kcontrol_new snd_cs4236_3d_controls_cs4235[] = {
 947CS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0),
 948CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1)
 949};
 950
 951static struct snd_kcontrol_new snd_cs4236_3d_controls_cs4237[] = {
 952CS4236_SINGLEC("3D Control - Switch", 0, 3, 7, 1, 0),
 953CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1),
 954CS4236_SINGLEC("3D Control - Center", 0, 2, 0, 15, 1),
 955CS4236_SINGLEC("3D Control - Mono", 0, 3, 6, 1, 0),
 956CS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0)
 957};
 958
 959static struct snd_kcontrol_new snd_cs4236_3d_controls_cs4238[] = {
 960CS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0),
 961CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1),
 962CS4236_SINGLEC("3D Control - Volume", 0, 2, 0, 15, 1),
 963CS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0)
 964};
 965
 966int snd_cs4236_mixer(struct snd_wss *chip)
 967{
 968        struct snd_card *card;
 969        unsigned int idx, count;
 970        int err;
 971        struct snd_kcontrol_new *kcontrol;
 972
 973        if (snd_BUG_ON(!chip || !chip->card))
 974                return -EINVAL;
 975        card = chip->card;
 976        strcpy(card->mixername, snd_wss_chip_id(chip));
 977
 978        if (chip->hardware == WSS_HW_CS4235 ||
 979            chip->hardware == WSS_HW_CS4239) {
 980                for (idx = 0; idx < ARRAY_SIZE(snd_cs4235_controls); idx++) {
 981                        if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4235_controls[idx], chip))) < 0)
 982                                return err;
 983                }
 984        } else {
 985                for (idx = 0; idx < ARRAY_SIZE(snd_cs4236_controls); idx++) {
 986                        if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_controls[idx], chip))) < 0)
 987                                return err;
 988                }
 989        }
 990        switch (chip->hardware) {
 991        case WSS_HW_CS4235:
 992        case WSS_HW_CS4239:
 993                count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4235);
 994                kcontrol = snd_cs4236_3d_controls_cs4235;
 995                break;
 996        case WSS_HW_CS4237B:
 997                count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4237);
 998                kcontrol = snd_cs4236_3d_controls_cs4237;
 999                break;
1000        case WSS_HW_CS4238B:
1001                count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4238);
1002                kcontrol = snd_cs4236_3d_controls_cs4238;
1003                break;
1004        default:
1005                count = 0;
1006                kcontrol = NULL;
1007        }
1008        for (idx = 0; idx < count; idx++, kcontrol++) {
1009                if ((err = snd_ctl_add(card, snd_ctl_new1(kcontrol, chip))) < 0)
1010                        return err;
1011        }
1012        if (chip->hardware == WSS_HW_CS4237B ||
1013            chip->hardware == WSS_HW_CS4238B) {
1014                for (idx = 0; idx < ARRAY_SIZE(snd_cs4236_iec958_controls); idx++) {
1015                        if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_iec958_controls[idx], chip))) < 0)
1016                                return err;
1017                }
1018        }
1019        return 0;
1020}
1021