dice.c 8.2 KB
Newer Older
C
Clemens Ladisch 已提交
1 2 3 4 5 6 7
/*
 * TC Applied Technologies Digital Interface Communications Engine driver
 *
 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
 * Licensed under the terms of the GNU General Public License, version 2.
 */

8
#include "dice.h"
C
Clemens Ladisch 已提交
9 10 11 12 13

MODULE_DESCRIPTION("DICE driver");
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_LICENSE("GPL v2");

14 15 16 17
#define OUI_WEISS		0x001c6a

#define DICE_CATEGORY_ID	0x04
#define WEISS_CATEGORY_ID	0x00
18 19 20 21 22 23 24 25 26 27 28 29

static int dice_interface_check(struct fw_unit *unit)
{
	static const int min_values[10] = {
		10, 0x64 / 4,
		10, 0x18 / 4,
		10, 0x18 / 4,
		0, 0,
		0, 0,
	};
	struct fw_device *device = fw_parent_device(unit);
	struct fw_csr_iterator it;
30
	int key, val, vendor = -1, model = -1, err;
31
	unsigned int category, i;
32
	__be32 *pointers, value;
33
	__be32 tx_data[4];
34 35
	__be32 version;

36 37 38 39 40
	pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
				 GFP_KERNEL);
	if (pointers == NULL)
		return -ENOMEM;

41 42 43
	/*
	 * Check that GUID and unit directory are constructed according to DICE
	 * rules, i.e., that the specifier ID is the GUID's OUI, and that the
44 45
	 * GUID chip ID consists of the 8-bit category ID, the 10-bit product
	 * ID, and a 22-bit serial number.
46 47
	 */
	fw_csr_iterator_init(&it, unit->directory);
48
	while (fw_csr_iterator_next(&it, &key, &val)) {
49 50
		switch (key) {
		case CSR_SPECIFIER_ID:
51
			vendor = val;
52 53
			break;
		case CSR_MODEL:
54
			model = val;
55 56 57
			break;
		}
	}
58 59 60 61 62
	if (vendor == OUI_WEISS)
		category = WEISS_CATEGORY_ID;
	else
		category = DICE_CATEGORY_ID;
	if (device->config_rom[3] != ((vendor << 8) | category) ||
63 64 65 66
	    device->config_rom[4] >> 22 != model) {
		err = -ENODEV;
		goto end;
	}
67 68 69 70 71 72 73

	/*
	 * Check that the sub address spaces exist and are located inside the
	 * private address space.  The minimum values are chosen so that all
	 * minimally required registers are included.
	 */
	err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
74 75 76 77 78 79 80
				 DICE_PRIVATE_SPACE, pointers,
				 sizeof(__be32) * ARRAY_SIZE(min_values), 0);
	if (err < 0) {
		err = -ENODEV;
		goto end;
	}
	for (i = 0; i < ARRAY_SIZE(min_values); ++i) {
81
		value = be32_to_cpu(pointers[i]);
82 83 84 85
		if (value < min_values[i] || value >= 0x40000) {
			err = -ENODEV;
			goto end;
		}
86 87
	}

88 89 90 91 92
	/* We support playback only. Let capture devices be handled by FFADO. */
	err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
				 DICE_PRIVATE_SPACE +
				 be32_to_cpu(pointers[2]) * 4,
				 tx_data, sizeof(tx_data), 0);
93 94 95 96
	if (err < 0 || (tx_data[0] && tx_data[3])) {
		err = -ENODEV;
		goto end;
	}
97

98 99 100 101 102 103 104
	/*
	 * Check that the implemented DICE driver specification major version
	 * number matches.
	 */
	err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
				 DICE_PRIVATE_SPACE +
				 be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
105
				 &version, 4, 0);
106 107 108 109
	if (err < 0) {
		err = -ENODEV;
		goto end;
	}
110 111 112
	if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) {
		dev_err(&unit->device,
			"unknown DICE version: 0x%08x\n", be32_to_cpu(version));
113 114
		err = -ENODEV;
		goto end;
115
	}
116 117
end:
	return err;
118 119
}

120 121
static int highest_supported_mode_rate(struct snd_dice *dice,
				       unsigned int mode, unsigned int *rate)
122
{
123
	unsigned int i, m;
124

125 126 127 128 129 130 131 132 133
	for (i = ARRAY_SIZE(snd_dice_rates); i > 0; i--) {
		*rate = snd_dice_rates[i - 1];
		if (snd_dice_stream_get_rate_mode(dice, *rate, &m) < 0)
			continue;
		if (mode == m)
			break;
	}
	if (i == 0)
		return -EINVAL;
134

135
	return 0;
136 137
}

138
static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode)
139 140
{
	__be32 values[2];
141 142
	unsigned int rate;
	int err;
143

144
	if (highest_supported_mode_rate(dice, mode, &rate) < 0) {
145 146 147 148 149
		dice->rx_channels[mode] = 0;
		dice->rx_midi_ports[mode] = 0;
		return 0;
	}

150
	err = snd_dice_transaction_set_rate(dice, rate);
151 152 153
	if (err < 0)
		return err;

154 155
	err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO,
					   values, sizeof(values));
156 157 158 159 160 161 162 163 164
	if (err < 0)
		return err;

	dice->rx_channels[mode]   = be32_to_cpu(values[0]);
	dice->rx_midi_ports[mode] = be32_to_cpu(values[1]);

	return 0;
}

165
static int dice_read_params(struct snd_dice *dice)
C
Clemens Ladisch 已提交
166
{
167
	__be32 value;
168
	int mode, err;
C
Clemens Ladisch 已提交
169

170
	/* some very old firmwares don't tell about their clock support */
171 172 173 174
	if (dice->clock_caps > 0) {
		err = snd_dice_transaction_read_global(dice,
						GLOBAL_CLOCK_CAPABILITIES,
						&value, 4);
175 176 177 178 179 180 181 182 183 184 185
		if (err < 0)
			return err;
		dice->clock_caps = be32_to_cpu(value);
	} else {
		/* this should be supported by any device */
		dice->clock_caps = CLOCK_CAP_RATE_44100 |
				   CLOCK_CAP_RATE_48000 |
				   CLOCK_CAP_SOURCE_ARX1 |
				   CLOCK_CAP_SOURCE_INTERNAL;
	}

186 187 188 189 190 191
	for (mode = 2; mode >= 0; --mode) {
		err = dice_read_mode_params(dice, mode);
		if (err < 0)
			return err;
	}

C
Clemens Ladisch 已提交
192 193 194
	return 0;
}

195
static void dice_card_strings(struct snd_dice *dice)
C
Clemens Ladisch 已提交
196 197 198 199 200 201 202 203 204 205 206
{
	struct snd_card *card = dice->card;
	struct fw_device *dev = fw_parent_device(dice->unit);
	char vendor[32], model[32];
	unsigned int i;
	int err;

	strcpy(card->driver, "DICE");

	strcpy(card->shortname, "DICE");
	BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname));
207 208 209
	err = snd_dice_transaction_read_global(dice, GLOBAL_NICK_NAME,
					       card->shortname,
					       sizeof(card->shortname));
C
Clemens Ladisch 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222
	if (err >= 0) {
		/* DICE strings are returned in "always-wrong" endianness */
		BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0);
		for (i = 0; i < sizeof(card->shortname); i += 4)
			swab32s((u32 *)&card->shortname[i]);
		card->shortname[sizeof(card->shortname) - 1] = '\0';
	}

	strcpy(vendor, "?");
	fw_csr_string(dev->config_rom + 5, CSR_VENDOR, vendor, sizeof(vendor));
	strcpy(model, "?");
	fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model));
	snprintf(card->longname, sizeof(card->longname),
223 224
		 "%s %s (serial %u) at %s, S%d",
		 vendor, model, dev->config_rom[4] & 0x3fffff,
C
Clemens Ladisch 已提交
225 226 227 228 229
		 dev_name(&dice->unit->device), 100 << dev->max_speed);

	strcpy(card->mixername, "DICE");
}

230 231 232 233 234 235 236 237
static void dice_card_free(struct snd_card *card)
{
	struct snd_dice *dice = card->private_data;

	snd_dice_transaction_destroy(dice);
	mutex_destroy(&dice->mutex);
}

C
Clemens Ladisch 已提交
238 239 240
static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
{
	struct snd_card *card;
241
	struct snd_dice *dice;
C
Clemens Ladisch 已提交
242 243
	int err;

244 245
	err = dice_interface_check(unit);
	if (err < 0)
246
		goto end;
247

248 249
	err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
			   sizeof(*dice), &card);
C
Clemens Ladisch 已提交
250
	if (err < 0)
251
		goto end;
C
Clemens Ladisch 已提交
252 253 254

	dice = card->private_data;
	dice->card = card;
255 256 257
	dice->unit = unit;
	card->private_free = dice_card_free;

258
	spin_lock_init(&dice->lock);
C
Clemens Ladisch 已提交
259
	mutex_init(&dice->mutex);
260
	init_completion(&dice->clock_accepted);
261
	init_waitqueue_head(&dice->hwdep_wait);
C
Clemens Ladisch 已提交
262

263
	err = snd_dice_transaction_init(dice);
C
Clemens Ladisch 已提交
264
	if (err < 0)
265
		goto error;
266 267 268

	err = dice_read_params(dice);
	if (err < 0)
269
		goto error;
C
Clemens Ladisch 已提交
270 271 272

	dice_card_strings(dice);

273
	err = snd_dice_create_pcm(dice);
C
Clemens Ladisch 已提交
274 275 276
	if (err < 0)
		goto error;

277
	err = snd_dice_create_hwdep(dice);
C
Clemens Ladisch 已提交
278 279 280
	if (err < 0)
		goto error;

281
	snd_dice_create_proc(dice);
282

283
	err = snd_dice_stream_init(dice);
C
Clemens Ladisch 已提交
284 285 286
	if (err < 0)
		goto error;

287 288
	err = snd_card_register(card);
	if (err < 0) {
289
		snd_dice_stream_destroy(dice);
290 291
		goto error;
	}
C
Clemens Ladisch 已提交
292

293 294 295
	dev_set_drvdata(&unit->device, dice);
end:
	return err;
C
Clemens Ladisch 已提交
296 297 298 299 300 301 302
error:
	snd_card_free(card);
	return err;
}

static void dice_remove(struct fw_unit *unit)
{
303
	struct snd_dice *dice = dev_get_drvdata(&unit->device);
C
Clemens Ladisch 已提交
304

305 306
	snd_card_disconnect(dice->card);

307
	snd_dice_stream_destroy(dice);
308

C
Clemens Ladisch 已提交
309 310 311 312 313
	snd_card_free_when_closed(dice->card);
}

static void dice_bus_reset(struct fw_unit *unit)
{
314
	struct snd_dice *dice = dev_get_drvdata(&unit->device);
C
Clemens Ladisch 已提交
315

316 317 318
	/* The handler address register becomes initialized. */
	snd_dice_transaction_reinit(dice);

S
Stefan Richter 已提交
319
	mutex_lock(&dice->mutex);
320
	snd_dice_stream_update(dice);
C
Clemens Ladisch 已提交
321 322 323 324 325 326 327
	mutex_unlock(&dice->mutex);
}

#define DICE_INTERFACE	0x000001

static const struct ieee1394_device_id dice_id_table[] = {
	{
328 329
		.match_flags = IEEE1394_MATCH_VERSION,
		.version     = DICE_INTERFACE,
C
Clemens Ladisch 已提交
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
	},
	{ }
};
MODULE_DEVICE_TABLE(ieee1394, dice_id_table);

static struct fw_driver dice_driver = {
	.driver   = {
		.owner	= THIS_MODULE,
		.name	= KBUILD_MODNAME,
		.bus	= &fw_bus_type,
	},
	.probe    = dice_probe,
	.update   = dice_bus_reset,
	.remove   = dice_remove,
	.id_table = dice_id_table,
};

static int __init alsa_dice_init(void)
{
	return driver_register(&dice_driver.driver);
}

static void __exit alsa_dice_exit(void)
{
	driver_unregister(&dice_driver.driver);
}

module_init(alsa_dice_init);
module_exit(alsa_dice_exit);