Hi Takashi,
On Thu, Jul 03, 2003 at 02:29:01PM +0200, Takashi Iwai wrote:
> At Wed, 2 Jul 2003 15:29:55 -0500,
> Ryan Underwood wrote:
> >
> > 1) What is ALSA's policy on module options? For example, would it be
> > okay to add an option like "thinkpad" to the ad1848 module, so that
> > inserting it with "thinkpad=1" would cause the necessary ports to be
> > prodded?
>
> this looks like a feasible solution.
>
> > 2) Since the chip's power mode can be controlled through a write to that
> > port (bit 2 on, cs4248 power on; bit 2 off, cs4248 power off), would it
> > be a good idea to add the _suspend, _resume, and _set_power_state
> > functions within a #ifdef CONFIG_PM block, like in other drivers?
>
> yep. we haven't done that just becase of lack of hardware.
> if you can write and test it, it would be greatly appreciated.
I have attached a patch against the ALSA 0.94. ad1848.c, ad1848_lib.c,
and ad1848.h have changed.
I tested the module against 2.4.21 on a Debian system, gcc 3.3 and it
works fine; simply set the proper resources and thinkpad=1 and the
thinkpad works.
One issue is to note, I don't know if this is a problem with the ad1848
driver in general or what; if I cat a 11025hz mono 8-bit file to
/dev/dsp, the sound comes out verrrrry slllloooowwwllly. But using
aplay, it works fine.
I am not sure the best way to test the power managmeent code -- it
doesn't crash the machine at least. ;)
I added a note to the ALSA wiki about this option. I tried to fix the
OSS driver too but it does some other weird things like not allowing a
IRQ > 7 to be used. (It thinks the CS4248 is an 8bit card or
something). So this will have to be an alsa-only fix for now at least.
Let me know if the patch is ok,
--
Ryan Underwood, <nemesis at icequake.net>, icq=10317253
diff -ur alsa-driver/alsa-kernel/include/ad1848.h alsanew/alsa-kernel/include/ad1848.h
--- alsa-driver/alsa-kernel/include/ad1848.h 2003-04-23 05:01:29.000000000 -0500
+++ alsanew/alsa-kernel/include/ad1848.h 2003-07-03 20:29:59.000000000 -0500
@@ -121,6 +121,11 @@
#define AD1848_HW_CS4248 0x0003 /* CS4248 chip */
#define AD1848_HW_CMI8330 0x0004 /* CMI8330 chip */
+/* IBM Thinkpad specific stuff */
+#define AD1848_THINKPAD_CTL_PORT1 0x15e8
+#define AD1848_THINKPAD_CTL_PORT2 0x15e9
+#define AD1848_THINKPAD_CS4248_ENABLE_BIT 0x02
+
struct _snd_ad1848 {
unsigned long port; /* i/o port */
struct resource *res_port;
@@ -140,6 +145,10 @@
int mce_bit;
int calibrate_mute;
int dma_size;
+ int thinkpad_flag; /* Thinkpad CS4248 needs some extra help */
+#ifdef CONFIG_PM
+ struct pm_dev *thinkpad_pmstate;
+#endif
spinlock_t reg_lock;
struct semaphore open_mutex;
@@ -157,7 +166,7 @@
int snd_ad1848_create(snd_card_t * card,
unsigned long port,
- int irq, int dma,
+ int irq, int dma, int thinkpad,
unsigned short hardware,
ad1848_t ** chip);
diff -ur alsa-driver/alsa-kernel/isa/ad1848/ad1848.c
alsanew/alsa-kernel/isa/ad1848/ad1848.c
--- alsa-driver/alsa-kernel/isa/ad1848/ad1848.c 2002-10-21 13:28:21.000000000 -0500
+++ alsanew/alsa-kernel/isa/ad1848/ad1848.c 2003-07-07 13:11:55.000000000 -0500
@@ -46,6 +46,7 @@
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */
+static int thinkpad[SNDRV_CARDS]; /* Thinkpad special case */
MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
MODULE_PARM_DESC(index, "Index value for AD1848 soundcard.");
@@ -65,6 +66,9 @@
MODULE_PARM(dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
MODULE_PARM_DESC(dma1, "DMA1 # for AD1848 driver.");
MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC);
+MODULE_PARM(thinkpad, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(thinkpad, "Enable only for the onboard CS4248 of IBM Thinkpad
360/750/755 series.");
+MODULE_PARM_SYNTAX(thinkpad, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC);
static snd_card_t *snd_ad1848_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
@@ -96,6 +100,7 @@
if ((err = snd_ad1848_create(card, port[dev],
irq[dev],
dma1[dev],
+ thinkpad[dev],
AD1848_HW_DETECT,
&chip)) < 0) {
snd_card_free(card);
@@ -116,6 +121,10 @@
sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
pcm->name, chip->port, irq[dev], dma1[dev]);
+ if (thinkpad[dev]) {
+ strcat(card->longname, " [Thinkpad]");
+ }
+
if ((err = snd_card_register(card)) < 0) {
snd_card_free(card);
return err;
@@ -168,7 +177,8 @@
get_id(&str,&id[nr_dev]) == 2 &&
get_option(&str,(int *)&port[nr_dev]) == 2 &&
get_option(&str,&irq[nr_dev]) == 2 &&
- get_option(&str,&dma1[nr_dev]) == 2);
+ get_option(&str,&dma1[nr_dev]) == 2 &&
+ get_option(&str,&thinkpad[nr_dev]) == 2);
nr_dev++;
return 1;
}
diff -ur alsa-driver/alsa-kernel/isa/ad1848/ad1848_lib.c
alsanew/alsa-kernel/isa/ad1848/ad1848_lib.c
--- alsa-driver/alsa-kernel/isa/ad1848/ad1848_lib.c 2003-05-30 07:28:34.000000000
-0500
+++ alsanew/alsa-kernel/isa/ad1848/ad1848_lib.c 2003-07-03 20:25:21.000000000 -0500
@@ -625,6 +625,92 @@
*/
+static void snd_ad1848_thinkpad_twiddle(ad1848_t *chip, int on) {
+
+ int tmp;
+
+ if (!chip->thinkpad_flag) return;
+
+ outb(0x1c, AD1848_THINKPAD_CTL_PORT1);
+ tmp = inb(AD1848_THINKPAD_CTL_PORT2);
+
+ switch (on) {
+ case 0: /* turn it off */
+ tmp &= ~AD1848_THINKPAD_CS4248_ENABLE_BIT;
+ default: /* turn it on */
+ tmp |= AD1848_THINKPAD_CS4248_ENABLE_BIT;
+ }
+
+ outb(tmp, AD1848_THINKPAD_CTL_PORT2);
+
+}
+
+#ifdef CONFIG_PM
+static void snd_ad1848_suspend(ad1848_t *chip) {
+
+ snd_card_t *card = chip->card;
+
+ if (card->power_state == SNDRV_CTL_POWER_D3hot)
+ return;
+
+ if (chip->thinkpad_flag) {
+ snd_pcm_suspend_all(chip->pcm);
+ snd_ad1848_thinkpad_twiddle(chip, 0);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ }
+
+}
+
+static void snd_ad1848_resume(ad1848_t *chip) {
+
+ snd_card_t *card = chip->card;
+
+ if (card->power_state == SNDRV_CTL_POWER_D0)
+ return;
+
+ if (chip->thinkpad_flag) {
+ snd_ad1848_thinkpad_twiddle(chip, 1);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ }
+}
+
+/* callback for control API */
+static int snd_ad1848_set_power_state(snd_card_t *card, unsigned int power_state)
+{
+ ad1848_t *chip = (ad1848_t *) card->power_state_private_data;
+ switch (power_state) {
+ case SNDRV_CTL_POWER_D0:
+ case SNDRV_CTL_POWER_D1:
+ case SNDRV_CTL_POWER_D2:
+ snd_ad1848_resume(chip);
+ break;
+ case SNDRV_CTL_POWER_D3hot:
+ case SNDRV_CTL_POWER_D3cold:
+ snd_ad1848_suspend(chip);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int snd_ad1848_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+ ad1848_t *chip = snd_magic_cast(ad1848_t, dev->data, return 0);
+
+ switch (rqst) {
+ case PM_SUSPEND:
+ snd_ad1848_suspend(chip);
+ break;
+ case PM_RESUME:
+ snd_ad1848_resume(chip);
+ break;
+ }
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
static int snd_ad1848_probe(ad1848_t * chip)
{
unsigned long flags;
@@ -799,6 +885,10 @@
static int snd_ad1848_free(ad1848_t *chip)
{
+#ifdef CONFIG_PM
+ if (chip->thinkpad_flag && chip->thinkpad_pmstate)
+ pm_unregister(chip->thinkpad_pmstate);
+#endif
if (chip->res_port) {
release_resource(chip->res_port);
kfree_nocheck(chip->res_port);
@@ -832,7 +922,7 @@
int snd_ad1848_create(snd_card_t * card,
unsigned long port,
- int irq, int dma,
+ int irq, int dma, int thinkpad,
unsigned short hardware,
ad1848_t ** rchip)
{
@@ -870,6 +960,22 @@
}
chip->dma = dma;
+ if (thinkpad) {
+ chip->thinkpad_flag = 1;
+ snd_ad1848_thinkpad_twiddle(chip, 1);
+#ifdef CONFIG_PM
+ chip->thinkpad_pmstate = pm_register(PM_ISA_DEV, 0,
snd_ad1848_pm_callback);
+ if (chip->thinkpad_pmstate) {
+ chip->thinkpad_pmstate->data = chip;
+ card->set_power_state = snd_ad1848_set_power_state; /*
callback */
+ card->power_state_private_data = chip;
+ }
+#endif
+ }
+ else {
+ chip->thinkpad_flag = 0;
+ }
+
if (snd_ad1848_probe(chip) < 0) {
snd_ad1848_free(chip);
return -ENODEV;