vx222.c 7.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * Driver for Digigram VX222 V2/Mic PCI soundcards
 *
 * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */

#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
25
#include <linux/module.h>
L
Linus Torvalds 已提交
26 27
#include <sound/core.h>
#include <sound/initval.h>
28
#include <sound/tlv.h>
L
Linus Torvalds 已提交
29 30 31 32 33 34 35 36 37 38 39
#include "vx222.h"

#define CARD_NAME "VX222"

MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("Digigram VX222 V2/Mic");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Digigram," CARD_NAME "}}");

static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
40 41
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
static bool mic[SNDRV_CARDS]; /* microphone */
L
Linus Torvalds 已提交
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
static int ibl[SNDRV_CARDS]; /* microphone */

module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Digigram " CARD_NAME " soundcard.");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for Digigram " CARD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard.");
module_param_array(mic, bool, NULL, 0444);
MODULE_PARM_DESC(mic, "Enable Microphone.");
module_param_array(ibl, int, NULL, 0444);
MODULE_PARM_DESC(ibl, "Capture IBL size.");

/*
 */

enum {
	VX_PCI_VX222_OLD,
	VX_PCI_VX222_NEW
};

63
static DEFINE_PCI_DEVICE_TABLE(snd_vx222_ids) = {
L
Linus Torvalds 已提交
64 65 66 67 68 69 70 71 72 73 74
	{ 0x10b5, 0x9050, 0x1369, PCI_ANY_ID, 0, 0, VX_PCI_VX222_OLD, },   /* PLX */
	{ 0x10b5, 0x9030, 0x1369, PCI_ANY_ID, 0, 0, VX_PCI_VX222_NEW, },   /* PLX */
	{ 0, }
};

MODULE_DEVICE_TABLE(pci, snd_vx222_ids);


/*
 */

75 76
static const DECLARE_TLV_DB_SCALE(db_scale_old_vol, -11350, 50, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_akm, -7350, 50, 0);
77

L
Linus Torvalds 已提交
78 79 80 81 82 83 84 85 86
static struct snd_vx_hardware vx222_old_hw = {

	.name = "VX222/Old",
	.type = VX_TYPE_BOARD,
	/* hw specs */
	.num_codecs = 1,
	.num_ins = 1,
	.num_outs = 1,
	.output_level_max = VX_ANALOG_OUT_LEVEL_MAX,
87
	.output_level_db_scale = db_scale_old_vol,
L
Linus Torvalds 已提交
88 89 90 91 92 93 94 95 96 97 98
};

static struct snd_vx_hardware vx222_v2_hw = {

	.name = "VX222/v2",
	.type = VX_TYPE_V2,
	/* hw specs */
	.num_codecs = 1,
	.num_ins = 1,
	.num_outs = 1,
	.output_level_max = VX2_AKM_LEVEL_MAX,
99
	.output_level_db_scale = db_scale_akm,
L
Linus Torvalds 已提交
100 101 102 103 104 105 106 107 108 109 110
};

static struct snd_vx_hardware vx222_mic_hw = {

	.name = "VX222/Mic",
	.type = VX_TYPE_MIC,
	/* hw specs */
	.num_codecs = 1,
	.num_ins = 1,
	.num_outs = 1,
	.output_level_max = VX2_AKM_LEVEL_MAX,
111
	.output_level_db_scale = db_scale_akm,
L
Linus Torvalds 已提交
112 113 114 115 116
};


/*
 */
117
static int snd_vx222_free(struct vx_core *chip)
L
Linus Torvalds 已提交
118 119 120 121 122 123 124 125 126 127 128 129
{
	struct snd_vx222 *vx = (struct snd_vx222 *)chip;

	if (chip->irq >= 0)
		free_irq(chip->irq, (void*)chip);
	if (vx->port[0])
		pci_release_regions(vx->pci);
	pci_disable_device(vx->pci);
	kfree(chip);
	return 0;
}

130
static int snd_vx222_dev_free(struct snd_device *device)
L
Linus Torvalds 已提交
131
{
132
	struct vx_core *chip = device->device_data;
L
Linus Torvalds 已提交
133 134 135 136
	return snd_vx222_free(chip);
}


137 138 139
static int snd_vx222_create(struct snd_card *card, struct pci_dev *pci,
			    struct snd_vx_hardware *hw,
			    struct snd_vx222 **rchip)
L
Linus Torvalds 已提交
140
{
141
	struct vx_core *chip;
L
Linus Torvalds 已提交
142 143
	struct snd_vx222 *vx;
	int i, err;
144
	static struct snd_device_ops ops = {
L
Linus Torvalds 已提交
145 146 147 148 149 150 151 152 153 154 155
		.dev_free =	snd_vx222_dev_free,
	};
	struct snd_vx_ops *vx_ops;

	/* enable PCI device */
	if ((err = pci_enable_device(pci)) < 0)
		return err;
	pci_set_master(pci);

	vx_ops = hw->type == VX_TYPE_BOARD ? &vx222_old_ops : &vx222_ops;
	chip = snd_vx_create(card, hw, vx_ops,
156
			     sizeof(struct snd_vx222) - sizeof(struct vx_core));
L
Linus Torvalds 已提交
157 158 159 160 161 162 163 164 165 166 167 168 169 170
	if (! chip) {
		pci_disable_device(pci);
		return -ENOMEM;
	}
	vx = (struct snd_vx222 *)chip;
	vx->pci = pci;

	if ((err = pci_request_regions(pci, CARD_NAME)) < 0) {
		snd_vx222_free(chip);
		return err;
	}
	for (i = 0; i < 2; i++)
		vx->port[i] = pci_resource_start(pci, i + 1);

171
	if (request_irq(pci->irq, snd_vx_irq_handler, IRQF_SHARED,
172
			KBUILD_MODNAME, chip)) {
L
Linus Torvalds 已提交
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
		snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
		snd_vx222_free(chip);
		return -EBUSY;
	}
	chip->irq = pci->irq;

	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
		snd_vx222_free(chip);
		return err;
	}

	snd_card_set_dev(card, &pci->dev);

	*rchip = vx;
	return 0;
}


191 192
static int snd_vx222_probe(struct pci_dev *pci,
			   const struct pci_device_id *pci_id)
L
Linus Torvalds 已提交
193 194
{
	static int dev;
195
	struct snd_card *card;
L
Linus Torvalds 已提交
196 197 198 199 200 201 202 203 204 205 206
	struct snd_vx_hardware *hw;
	struct snd_vx222 *vx;
	int err;

	if (dev >= SNDRV_CARDS)
		return -ENODEV;
	if (!enable[dev]) {
		dev++;
		return -ENOENT;
	}

207 208 209
	err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
	if (err < 0)
		return err;
L
Linus Torvalds 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226

	switch ((int)pci_id->driver_data) {
	case VX_PCI_VX222_OLD:
		hw = &vx222_old_hw;
		break;
	case VX_PCI_VX222_NEW:
	default:
		if (mic[dev])
			hw = &vx222_mic_hw;
		else
			hw = &vx222_v2_hw;
		break;
	}
	if ((err = snd_vx222_create(card, pci, hw, &vx)) < 0) {
		snd_card_free(card);
		return err;
	}
T
Takashi Iwai 已提交
227
	card->private_data = vx;
L
Linus Torvalds 已提交
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
	vx->core.ibl.size = ibl[dev];

	sprintf(card->longname, "%s at 0x%lx & 0x%lx, irq %i",
		card->shortname, vx->port[0], vx->port[1], vx->core.irq);
	snd_printdd("%s at 0x%lx & 0x%lx, irq %i\n",
		    card->shortname, vx->port[0], vx->port[1], vx->core.irq);

#ifdef SND_VX_FW_LOADER
	vx->core.dev = &pci->dev;
#endif

	if ((err = snd_vx_setup_firmware(&vx->core)) < 0) {
		snd_card_free(card);
		return err;
	}

	if ((err = snd_card_register(card)) < 0) {
		snd_card_free(card);
		return err;
	}

	pci_set_drvdata(pci, card);
	dev++;
	return 0;
}

254
static void snd_vx222_remove(struct pci_dev *pci)
L
Linus Torvalds 已提交
255 256 257 258 259
{
	snd_card_free(pci_get_drvdata(pci));
	pci_set_drvdata(pci, NULL);
}

260
#ifdef CONFIG_PM_SLEEP
261
static int snd_vx222_suspend(struct device *dev)
T
Takashi Iwai 已提交
262
{
263 264
	struct pci_dev *pci = to_pci_dev(dev);
	struct snd_card *card = dev_get_drvdata(dev);
T
Takashi Iwai 已提交
265 266 267
	struct snd_vx222 *vx = card->private_data;
	int err;

268
	err = snd_vx_suspend(&vx->core);
T
Takashi Iwai 已提交
269 270
	pci_disable_device(pci);
	pci_save_state(pci);
271
	pci_set_power_state(pci, PCI_D3hot);
T
Takashi Iwai 已提交
272 273 274
	return err;
}

275
static int snd_vx222_resume(struct device *dev)
T
Takashi Iwai 已提交
276
{
277 278
	struct pci_dev *pci = to_pci_dev(dev);
	struct snd_card *card = dev_get_drvdata(dev);
T
Takashi Iwai 已提交
279 280 281
	struct snd_vx222 *vx = card->private_data;

	pci_set_power_state(pci, PCI_D0);
282 283 284 285 286 287 288
	pci_restore_state(pci);
	if (pci_enable_device(pci) < 0) {
		printk(KERN_ERR "vx222: pci_enable_device failed, "
		       "disabling device\n");
		snd_card_disconnect(card);
		return -EIO;
	}
T
Takashi Iwai 已提交
289 290 291
	pci_set_master(pci);
	return snd_vx_resume(&vx->core);
}
292 293 294 295 296

static SIMPLE_DEV_PM_OPS(snd_vx222_pm, snd_vx222_suspend, snd_vx222_resume);
#define SND_VX222_PM_OPS	&snd_vx222_pm
#else
#define SND_VX222_PM_OPS	NULL
T
Takashi Iwai 已提交
297 298
#endif

299
static struct pci_driver vx222_driver = {
300
	.name = KBUILD_MODNAME,
L
Linus Torvalds 已提交
301 302
	.id_table = snd_vx222_ids,
	.probe = snd_vx222_probe,
303
	.remove = snd_vx222_remove,
304 305 306
	.driver = {
		.pm = SND_VX222_PM_OPS,
	},
L
Linus Torvalds 已提交
307 308
};

309
module_pci_driver(vx222_driver);