[ALSA] switching rate in STAC9460 codec of Prodigy192
[safe/jmp/linux-2.6] / sound / i2c / other / ak4114.c
index f5e6018..facde46 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Routines for control of the AK4114 via I2C and 4-wire serial interface
  *  IEC958 (S/PDIF) receiver by Asahi Kasei
- *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
  *
  *
  *   This program is free software; you can redistribute it and/or modify
 #include <sound/ak4114.h>
 #include <sound/asoundef.h>
 
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
 MODULE_DESCRIPTION("AK4114 IEC958 (S/PDIF) receiver by Asahi Kasei");
 MODULE_LICENSE("GPL");
 
 #define AK4114_ADDR                    0x00 /* fixed address */
 
-static void ak4114_stats(void *);
+static void ak4114_stats(struct work_struct *work);
+static void ak4114_init_regs(struct ak4114 *chip);
 
-static void reg_write(ak4114_t *ak4114, unsigned char reg, unsigned char val)
+static void reg_write(struct ak4114 *ak4114, unsigned char reg, unsigned char val)
 {
        ak4114->write(ak4114->private_data, reg, val);
        if (reg <= AK4114_REG_INT1_MASK)
                ak4114->regmap[reg] = val;
-       else if (reg >= AK4114_REG_RXCSB0 && reg <= AK4114_REG_TXCSB4)
-               ak4114->txcsb[reg-AK4114_REG_RXCSB0] = val;
+       else if (reg >= AK4114_REG_TXCSB0 && reg <= AK4114_REG_TXCSB4)
+               ak4114->txcsb[reg-AK4114_REG_TXCSB0] = val;
 }
 
-static inline unsigned char reg_read(ak4114_t *ak4114, unsigned char reg)
+static inline unsigned char reg_read(struct ak4114 *ak4114, unsigned char reg)
 {
        return ak4114->read(ak4114->private_data, reg);
 }
 
 #if 0
-static void reg_dump(ak4114_t *ak4114)
+static void reg_dump(struct ak4114 *ak4114)
 {
        int i;
 
-       printk("AK4114 REG DUMP:\n");
+       printk(KERN_DEBUG "AK4114 REG DUMP:\n");
        for (i = 0; i < 0x20; i++)
-               printk("reg[%02x] = %02x (%02x)\n", i, reg_read(ak4114, i), i < sizeof(ak4114->regmap) ? ak4114->regmap[i] : 0);
+               printk(KERN_DEBUG "reg[%02x] = %02x (%02x)\n", i, reg_read(ak4114, i), i < sizeof(ak4114->regmap) ? ak4114->regmap[i] : 0);
 }
 #endif
 
-static void snd_ak4114_free(ak4114_t *chip)
+static void snd_ak4114_free(struct ak4114 *chip)
 {
        chip->init = 1; /* don't schedule new work */
        mb();
-       if (chip->workqueue != NULL) {
-               flush_workqueue(chip->workqueue);
-               destroy_workqueue(chip->workqueue);
-       }
+       cancel_delayed_work(&chip->work);
+       flush_scheduled_work();
        kfree(chip);
 }
 
-static int snd_ak4114_dev_free(snd_device_t *device)
+static int snd_ak4114_dev_free(struct snd_device *device)
 {
-       ak4114_t *chip = device->device_data;
+       struct ak4114 *chip = device->device_data;
        snd_ak4114_free(chip);
        return 0;
 }
 
-int snd_ak4114_create(snd_card_t *card,
+int snd_ak4114_create(struct snd_card *card,
                      ak4114_read_t *read, ak4114_write_t *write,
-                     unsigned char pgm[7], unsigned char txcsb[5],
-                     void *private_data, ak4114_t **r_ak4114)
+                     const unsigned char pgm[7], const unsigned char txcsb[5],
+                     void *private_data, struct ak4114 **r_ak4114)
 {
-       ak4114_t *chip;
+       struct ak4114 *chip;
        int err = 0;
        unsigned char reg;
-       static snd_device_ops_t ops = {
+       static struct snd_device_ops ops = {
                .dev_free =     snd_ak4114_dev_free,
        };
 
-       chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
        if (chip == NULL)
                return -ENOMEM;
        spin_lock_init(&chip->lock);
@@ -100,19 +99,14 @@ int snd_ak4114_create(snd_card_t *card,
        chip->read = read;
        chip->write = write;
        chip->private_data = private_data;
+       INIT_DELAYED_WORK(&chip->work, ak4114_stats);
 
        for (reg = 0; reg < 7; reg++)
                chip->regmap[reg] = pgm[reg];
        for (reg = 0; reg < 5; reg++)
                chip->txcsb[reg] = txcsb[reg];
 
-       chip->workqueue = create_workqueue("snd-ak4114");
-       if (chip->workqueue == NULL) {
-               kfree(chip);
-               return -ENOMEM;
-       }
-
-       snd_ak4114_reinit(chip);
+       ak4114_init_regs(chip);
 
        chip->rcs0 = reg_read(chip, AK4114_REG_RCS0) & ~(AK4114_QINT | AK4114_CINT);
        chip->rcs1 = reg_read(chip, AK4114_REG_RCS1);
@@ -129,21 +123,19 @@ int snd_ak4114_create(snd_card_t *card,
        return err < 0 ? err : -EIO;
 }
 
-void snd_ak4114_reg_write(ak4114_t *chip, unsigned char reg, unsigned char mask, unsigned char val)
+void snd_ak4114_reg_write(struct ak4114 *chip, unsigned char reg, unsigned char mask, unsigned char val)
 {
        if (reg <= AK4114_REG_INT1_MASK)
                reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val);
        else if (reg >= AK4114_REG_TXCSB0 && reg <= AK4114_REG_TXCSB4)
-               reg_write(chip, reg, (chip->txcsb[reg] & ~mask) | val);
+               reg_write(chip, reg,
+                         (chip->txcsb[reg-AK4114_REG_TXCSB0] & ~mask) | val);
 }
 
-void snd_ak4114_reinit(ak4114_t *chip)
+static void ak4114_init_regs(struct ak4114 *chip)
 {
        unsigned char old = chip->regmap[AK4114_REG_PWRDN], reg;
 
-       chip->init = 1;
-       mb();
-       flush_workqueue(chip->workqueue);
        /* bring the chip to reset state and powerdown state */
        reg_write(chip, AK4114_REG_PWRDN, old & ~(AK4114_RST|AK4114_PWN));
        udelay(200);
@@ -156,10 +148,18 @@ void snd_ak4114_reinit(ak4114_t *chip)
                reg_write(chip, reg + AK4114_REG_TXCSB0, chip->txcsb[reg]);
        /* release powerdown, everything is initialized now */
        reg_write(chip, AK4114_REG_PWRDN, old | AK4114_RST | AK4114_PWN);
+}
+
+void snd_ak4114_reinit(struct ak4114 *chip)
+{
+       chip->init = 1;
+       mb();
+       flush_scheduled_work();
+       ak4114_init_regs(chip);
        /* bring up statistics / event queing */
        chip->init = 0;
-       INIT_WORK(&chip->work, ak4114_stats, chip);
-       queue_delayed_work(chip->workqueue, &chip->work, HZ / 10);
+       if (chip->kctls[0])
+               schedule_delayed_work(&chip->work, HZ / 10);
 }
 
 static unsigned int external_rate(unsigned char rcs1)
@@ -176,8 +176,8 @@ static unsigned int external_rate(unsigned char rcs1)
        }
 }
 
-static int snd_ak4114_in_error_info(snd_kcontrol_t *kcontrol,
-                                   snd_ctl_elem_info_t *uinfo)
+static int snd_ak4114_in_error_info(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_info *uinfo)
 {
        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
        uinfo->count = 1;
@@ -186,10 +186,10 @@ static int snd_ak4114_in_error_info(snd_kcontrol_t *kcontrol,
        return 0;
 }
 
-static int snd_ak4114_in_error_get(snd_kcontrol_t *kcontrol,
-                                  snd_ctl_elem_value_t *ucontrol)
+static int snd_ak4114_in_error_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
 {
-       ak4114_t *chip = snd_kcontrol_chip(kcontrol);
+       struct ak4114 *chip = snd_kcontrol_chip(kcontrol);
        long *ptr;
 
        spin_lock_irq(&chip->lock);
@@ -200,20 +200,12 @@ static int snd_ak4114_in_error_get(snd_kcontrol_t *kcontrol,
        return 0;
 }
 
-static int snd_ak4114_in_bit_info(snd_kcontrol_t *kcontrol,
-                                 snd_ctl_elem_info_t *uinfo)
-{
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-       uinfo->count = 1;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = 1;
-       return 0;
-}
+#define snd_ak4114_in_bit_info         snd_ctl_boolean_mono_info
 
-static int snd_ak4114_in_bit_get(snd_kcontrol_t *kcontrol,
-                                snd_ctl_elem_value_t *ucontrol)
+static int snd_ak4114_in_bit_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
 {
-       ak4114_t *chip = snd_kcontrol_chip(kcontrol);
+       struct ak4114 *chip = snd_kcontrol_chip(kcontrol);
        unsigned char reg = kcontrol->private_value & 0xff;
        unsigned char bit = (kcontrol->private_value >> 8) & 0xff;
        unsigned char inv = (kcontrol->private_value >> 31) & 1;
@@ -222,8 +214,8 @@ static int snd_ak4114_in_bit_get(snd_kcontrol_t *kcontrol,
        return 0;
 }
 
-static int snd_ak4114_rate_info(snd_kcontrol_t *kcontrol,
-                               snd_ctl_elem_info_t *uinfo)
+static int snd_ak4114_rate_info(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_info *uinfo)
 {
        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
        uinfo->count = 1;
@@ -232,26 +224,26 @@ static int snd_ak4114_rate_info(snd_kcontrol_t *kcontrol,
        return 0;
 }
 
-static int snd_ak4114_rate_get(snd_kcontrol_t *kcontrol,
-                              snd_ctl_elem_value_t *ucontrol)
+static int snd_ak4114_rate_get(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
 {
-       ak4114_t *chip = snd_kcontrol_chip(kcontrol);
+       struct ak4114 *chip = snd_kcontrol_chip(kcontrol);
 
        ucontrol->value.integer.value[0] = external_rate(reg_read(chip, AK4114_REG_RCS1));
        return 0;
 }
 
-static int snd_ak4114_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+static int snd_ak4114_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
        uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
        uinfo->count = 1;
        return 0;
 }
 
-static int snd_ak4114_spdif_get(snd_kcontrol_t * kcontrol,
-                               snd_ctl_elem_value_t * ucontrol)
+static int snd_ak4114_spdif_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
 {
-       ak4114_t *chip = snd_kcontrol_chip(kcontrol);
+       struct ak4114 *chip = snd_kcontrol_chip(kcontrol);
        unsigned i;
 
        for (i = 0; i < AK4114_REG_RXCSB_SIZE; i++)
@@ -259,10 +251,10 @@ static int snd_ak4114_spdif_get(snd_kcontrol_t * kcontrol,
        return 0;
 }
 
-static int snd_ak4114_spdif_playback_get(snd_kcontrol_t * kcontrol,
-                                        snd_ctl_elem_value_t * ucontrol)
+static int snd_ak4114_spdif_playback_get(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
 {
-       ak4114_t *chip = snd_kcontrol_chip(kcontrol);
+       struct ak4114 *chip = snd_kcontrol_chip(kcontrol);
        unsigned i;
 
        for (i = 0; i < AK4114_REG_TXCSB_SIZE; i++)
@@ -270,10 +262,10 @@ static int snd_ak4114_spdif_playback_get(snd_kcontrol_t * kcontrol,
        return 0;
 }
 
-static int snd_ak4114_spdif_playback_put(snd_kcontrol_t * kcontrol,
-                                        snd_ctl_elem_value_t * ucontrol)
+static int snd_ak4114_spdif_playback_put(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
 {
-       ak4114_t *chip = snd_kcontrol_chip(kcontrol);
+       struct ak4114 *chip = snd_kcontrol_chip(kcontrol);
        unsigned i;
 
        for (i = 0; i < AK4114_REG_TXCSB_SIZE; i++)
@@ -281,21 +273,21 @@ static int snd_ak4114_spdif_playback_put(snd_kcontrol_t * kcontrol,
        return 0;
 }
 
-static int snd_ak4114_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+static int snd_ak4114_spdif_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
        uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
        uinfo->count = 1;
        return 0;
 }
 
-static int snd_ak4114_spdif_mask_get(snd_kcontrol_t * kcontrol,
-                                     snd_ctl_elem_value_t * ucontrol)
+static int snd_ak4114_spdif_mask_get(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
 {
        memset(ucontrol->value.iec958.status, 0xff, AK4114_REG_RXCSB_SIZE);
        return 0;
 }
 
-static int snd_ak4114_spdif_pinfo(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+static int snd_ak4114_spdif_pinfo(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
        uinfo->value.integer.min = 0;
@@ -304,10 +296,10 @@ static int snd_ak4114_spdif_pinfo(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t
        return 0;
 }
 
-static int snd_ak4114_spdif_pget(snd_kcontrol_t * kcontrol,
-                                snd_ctl_elem_value_t * ucontrol)
+static int snd_ak4114_spdif_pget(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
 {
-       ak4114_t *chip = snd_kcontrol_chip(kcontrol);
+       struct ak4114 *chip = snd_kcontrol_chip(kcontrol);
        unsigned short tmp;
 
        ucontrol->value.integer.value[0] = 0xf8f2;
@@ -319,17 +311,17 @@ static int snd_ak4114_spdif_pget(snd_kcontrol_t * kcontrol,
        return 0;
 }
 
-static int snd_ak4114_spdif_qinfo(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+static int snd_ak4114_spdif_qinfo(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
        uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
        uinfo->count = AK4114_REG_QSUB_SIZE;
        return 0;
 }
 
-static int snd_ak4114_spdif_qget(snd_kcontrol_t * kcontrol,
-                                snd_ctl_elem_value_t * ucontrol)
+static int snd_ak4114_spdif_qget(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
 {
-       ak4114_t *chip = snd_kcontrol_chip(kcontrol);
+       struct ak4114 *chip = snd_kcontrol_chip(kcontrol);
        unsigned i;
 
        for (i = 0; i < AK4114_REG_QSUB_SIZE; i++)
@@ -338,14 +330,14 @@ static int snd_ak4114_spdif_qget(snd_kcontrol_t * kcontrol,
 }
 
 /* Don't forget to change AK4114_CONTROLS define!!! */
-static snd_kcontrol_new_t snd_ak4114_iec958_controls[] = {
+static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = {
 {
        .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
        .name =         "IEC958 Parity Errors",
        .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
        .info =         snd_ak4114_in_error_info,
        .get =          snd_ak4114_in_error_get,
-       .private_value = offsetof(ak4114_t, parity_errors),
+       .private_value = offsetof(struct ak4114, parity_errors),
 },
 {
        .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
@@ -353,7 +345,7 @@ static snd_kcontrol_new_t snd_ak4114_iec958_controls[] = {
        .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
        .info =         snd_ak4114_in_error_info,
        .get =          snd_ak4114_in_error_get,
-       .private_value = offsetof(ak4114_t, v_bit_errors),
+       .private_value = offsetof(struct ak4114, v_bit_errors),
 },
 {
        .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
@@ -361,7 +353,7 @@ static snd_kcontrol_new_t snd_ak4114_iec958_controls[] = {
        .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
        .info =         snd_ak4114_in_error_info,
        .get =          snd_ak4114_in_error_get,
-       .private_value = offsetof(ak4114_t, ccrc_errors),
+       .private_value = offsetof(struct ak4114, ccrc_errors),
 },
 {
        .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
@@ -369,7 +361,7 @@ static snd_kcontrol_new_t snd_ak4114_iec958_controls[] = {
        .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
        .info =         snd_ak4114_in_error_info,
        .get =          snd_ak4114_in_error_get,
-       .private_value = offsetof(ak4114_t, qcrc_errors),
+       .private_value = offsetof(struct ak4114, qcrc_errors),
 },
 {
        .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
@@ -435,7 +427,7 @@ static snd_kcontrol_new_t snd_ak4114_iec958_controls[] = {
        .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
        .info =         snd_ak4114_in_bit_info,
        .get =          snd_ak4114_in_bit_get,
-       .private_value = (6<<8) | AK4114_REG_RCS1,
+       .private_value = (6<<8) | AK4114_REG_RCS0,
 },
 {
        .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
@@ -443,15 +435,23 @@ static snd_kcontrol_new_t snd_ak4114_iec958_controls[] = {
        .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
        .info =         snd_ak4114_in_bit_info,
        .get =          snd_ak4114_in_bit_get,
-       .private_value = (3<<8) | AK4114_REG_RCS1,
+       .private_value = (3<<8) | AK4114_REG_RCS0,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 PPL Lock Status",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4114_in_bit_info,
+       .get =          snd_ak4114_in_bit_get,
+       .private_value = (1<<31) | (4<<8) | AK4114_REG_RCS0,
 }
 };
 
-int snd_ak4114_build(ak4114_t *ak4114,
-                    snd_pcm_substream_t *ply_substream,
-                    snd_pcm_substream_t *cap_substream)
+int snd_ak4114_build(struct ak4114 *ak4114,
+                    struct snd_pcm_substream *ply_substream,
+                    struct snd_pcm_substream *cap_substream)
 {
-       snd_kcontrol_t *kctl;
+       struct snd_kcontrol *kctl;
        unsigned int idx;
        int err;
 
@@ -462,7 +462,7 @@ int snd_ak4114_build(ak4114_t *ak4114,
                kctl = snd_ctl_new1(&snd_ak4114_iec958_controls[idx], ak4114);
                if (kctl == NULL)
                        return -ENOMEM;
-               if (!strstr(kctl->id.name, "Playback")) {
+               if (strstr(kctl->id.name, "Playback")) {
                        if (ply_substream == NULL) {
                                snd_ctl_free_one(kctl);
                                ak4114->kctls[idx] = NULL;
@@ -479,10 +479,59 @@ int snd_ak4114_build(ak4114_t *ak4114,
                        return err;
                ak4114->kctls[idx] = kctl;
        }
+       /* trigger workq */
+       schedule_delayed_work(&ak4114->work, HZ / 10);
        return 0;
 }
 
-int snd_ak4114_external_rate(ak4114_t *ak4114)
+/* notify kcontrols if any parameters are changed */
+static void ak4114_notify(struct ak4114 *ak4114,
+                         unsigned char rcs0, unsigned char rcs1,
+                         unsigned char c0, unsigned char c1)
+{
+       if (!ak4114->kctls[0])
+               return;
+
+       if (rcs0 & AK4114_PAR)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              &ak4114->kctls[0]->id);
+       if (rcs0 & AK4114_V)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              &ak4114->kctls[1]->id);
+       if (rcs1 & AK4114_CCRC)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              &ak4114->kctls[2]->id);
+       if (rcs1 & AK4114_QCRC)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              &ak4114->kctls[3]->id);
+
+       /* rate change */
+       if (c1 & 0xf0)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              &ak4114->kctls[4]->id);
+
+       if ((c0 & AK4114_PEM) | (c0 & AK4114_CINT))
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              &ak4114->kctls[9]->id);
+       if (c0 & AK4114_QINT)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              &ak4114->kctls[10]->id);
+
+       if (c0 & AK4114_AUDION)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              &ak4114->kctls[11]->id);
+       if (c0 & AK4114_AUTO)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              &ak4114->kctls[12]->id);
+       if (c0 & AK4114_DTSCD)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              &ak4114->kctls[13]->id);
+       if (c0 & AK4114_UNLCK)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              &ak4114->kctls[14]->id);
+}
+
+int snd_ak4114_external_rate(struct ak4114 *ak4114)
 {
        unsigned char rcs1;
 
@@ -490,9 +539,9 @@ int snd_ak4114_external_rate(ak4114_t *ak4114)
        return external_rate(rcs1);
 }
 
-int snd_ak4114_check_rate_and_errors(ak4114_t *ak4114, unsigned int flags)
+int snd_ak4114_check_rate_and_errors(struct ak4114 *ak4114, unsigned int flags)
 {
-       snd_pcm_runtime_t *runtime = ak4114->capture_substream ? ak4114->capture_substream->runtime : NULL;
+       struct snd_pcm_runtime *runtime = ak4114->capture_substream ? ak4114->capture_substream->runtime : NULL;
        unsigned long _flags;
        int res = 0;
        unsigned char rcs0, rcs1;
@@ -518,31 +567,7 @@ int snd_ak4114_check_rate_and_errors(ak4114_t *ak4114, unsigned int flags)
        ak4114->rcs1 = rcs1;
        spin_unlock_irqrestore(&ak4114->lock, _flags);
 
-       if (rcs0 & AK4114_PAR)
-               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[0]->id);
-       if (rcs0 & AK4114_V)
-               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[1]->id);
-       if (rcs1 & AK4114_CCRC)
-               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[2]->id);
-       if (rcs1 & AK4114_QCRC)
-               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[3]->id);
-
-       /* rate change */
-       if (c1 & 0xf0)
-               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[4]->id);
-
-       if ((c0 & AK4114_PEM) | (c0 & AK4114_CINT))
-               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[9]->id);
-       if (c0 & AK4114_QINT)
-               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[10]->id);
-
-       if (c0 & AK4114_AUDION)
-               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[11]->id);
-       if (c0 & AK4114_AUTO)
-               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[12]->id);
-       if (c0 & AK4114_DTSCD)
-               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[13]->id);
-
+       ak4114_notify(ak4114, rcs0, rcs1, c0, c1);
        if (ak4114->change_callback && (c0 | c1) != 0)
                ak4114->change_callback(ak4114, c0, c1);
 
@@ -552,9 +577,8 @@ int snd_ak4114_check_rate_and_errors(ak4114_t *ak4114, unsigned int flags)
        if (!(flags & AK4114_CHECK_NO_RATE) && runtime && runtime->rate != res) {
                snd_pcm_stream_lock_irqsave(ak4114->capture_substream, _flags);
                if (snd_pcm_running(ak4114->capture_substream)) {
-                       // printk("rate changed (%i <- %i)\n", runtime->rate, res);
+                       // printk(KERN_DEBUG "rate changed (%i <- %i)\n", runtime->rate, res);
                        snd_pcm_stop(ak4114->capture_substream, SNDRV_PCM_STATE_DRAINING);
-                       wake_up(&runtime->sleep);
                        res = 1;
                }
                snd_pcm_stream_unlock_irqrestore(ak4114->capture_substream, _flags);
@@ -562,14 +586,14 @@ int snd_ak4114_check_rate_and_errors(ak4114_t *ak4114, unsigned int flags)
        return res;
 }
 
-static void ak4114_stats(void *data)
+static void ak4114_stats(struct work_struct *work)
 {
-       ak4114_t *chip = (ak4114_t *)data;
+       struct ak4114 *chip = container_of(work, struct ak4114, work.work);
 
-       if (chip->init)
-               return;
-       snd_ak4114_check_rate_and_errors(chip, 0);
-       queue_delayed_work(chip->workqueue, &chip->work, HZ / 10);
+       if (!chip->init)
+               snd_ak4114_check_rate_and_errors(chip, 0);
+
+       schedule_delayed_work(&chip->work, HZ / 10);
 }
 
 EXPORT_SYMBOL(snd_ak4114_create);