brintos

brintos / linux-shallow public Read only

0
0
Text · 8.6 KiB · 92941ef Raw
370 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * dice_transaction.c - a part of driver for Dice based devices4 *5 * Copyright (c) Clemens Ladisch6 * Copyright (c) 2014 Takashi Sakamoto7 */8 9#include "dice.h"10 11static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type,12		       u64 offset)13{14	switch (type) {15	case SND_DICE_ADDR_TYPE_TX:16		offset += dice->tx_offset;17		break;18	case SND_DICE_ADDR_TYPE_RX:19		offset += dice->rx_offset;20		break;21	case SND_DICE_ADDR_TYPE_SYNC:22		offset += dice->sync_offset;23		break;24	case SND_DICE_ADDR_TYPE_RSRV:25		offset += dice->rsrv_offset;26		break;27	case SND_DICE_ADDR_TYPE_GLOBAL:28	default:29		offset += dice->global_offset;30		break;31	}32	offset += DICE_PRIVATE_SPACE;33	return offset;34}35 36int snd_dice_transaction_write(struct snd_dice *dice,37			       enum snd_dice_addr_type type,38			       unsigned int offset, void *buf, unsigned int len)39{40	return snd_fw_transaction(dice->unit,41				  (len == 4) ? TCODE_WRITE_QUADLET_REQUEST :42					       TCODE_WRITE_BLOCK_REQUEST,43				  get_subaddr(dice, type, offset), buf, len, 0);44}45 46int snd_dice_transaction_read(struct snd_dice *dice,47			      enum snd_dice_addr_type type, unsigned int offset,48			      void *buf, unsigned int len)49{50	return snd_fw_transaction(dice->unit,51				  (len == 4) ? TCODE_READ_QUADLET_REQUEST :52					       TCODE_READ_BLOCK_REQUEST,53				  get_subaddr(dice, type, offset), buf, len, 0);54}55 56static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info)57{58	return snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,59						info, 4);60}61 62int snd_dice_transaction_get_clock_source(struct snd_dice *dice,63					  unsigned int *source)64{65	__be32 info;66	int err;67 68	err = get_clock_info(dice, &info);69	if (err >= 0)70		*source = be32_to_cpu(info) & CLOCK_SOURCE_MASK;71 72	return err;73}74 75int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)76{77	__be32 info;78	unsigned int index;79	int err;80 81	err = get_clock_info(dice, &info);82	if (err < 0)83		goto end;84 85	index = (be32_to_cpu(info) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT;86	if (index >= SND_DICE_RATES_COUNT) {87		err = -ENOSYS;88		goto end;89	}90 91	*rate = snd_dice_rates[index];92end:93	return err;94}95 96int snd_dice_transaction_set_enable(struct snd_dice *dice)97{98	__be32 value;99	int err = 0;100 101	if (dice->global_enabled)102		goto end;103 104	value = cpu_to_be32(1);105	err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,106				 get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,107					     GLOBAL_ENABLE),108				 &value, 4,109				 FW_FIXED_GENERATION | dice->owner_generation);110	if (err < 0)111		goto end;112 113	dice->global_enabled = true;114end:115	return err;116}117 118void snd_dice_transaction_clear_enable(struct snd_dice *dice)119{120	__be32 value;121 122	value = 0;123	snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,124			   get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,125				       GLOBAL_ENABLE),126			   &value, 4, FW_QUIET |127			   FW_FIXED_GENERATION | dice->owner_generation);128 129	dice->global_enabled = false;130}131 132static void dice_notification(struct fw_card *card, struct fw_request *request,133			      int tcode, int destination, int source,134			      int generation, unsigned long long offset,135			      void *data, size_t length, void *callback_data)136{137	struct snd_dice *dice = callback_data;138	u32 bits;139	unsigned long flags;140 141	if (tcode != TCODE_WRITE_QUADLET_REQUEST) {142		fw_send_response(card, request, RCODE_TYPE_ERROR);143		return;144	}145	if ((offset & 3) != 0) {146		fw_send_response(card, request, RCODE_ADDRESS_ERROR);147		return;148	}149 150	bits = be32_to_cpup(data);151 152	spin_lock_irqsave(&dice->lock, flags);153	dice->notification_bits |= bits;154	spin_unlock_irqrestore(&dice->lock, flags);155 156	fw_send_response(card, request, RCODE_COMPLETE);157 158	if (bits & NOTIFY_CLOCK_ACCEPTED)159		complete(&dice->clock_accepted);160	wake_up(&dice->hwdep_wait);161}162 163static int register_notification_address(struct snd_dice *dice, bool retry)164{165	struct fw_device *device = fw_parent_device(dice->unit);166	__be64 *buffer;167	unsigned int retries;168	int err;169 170	retries = (retry) ? 3 : 0;171 172	buffer = kmalloc(2 * 8, GFP_KERNEL);173	if (!buffer)174		return -ENOMEM;175 176	for (;;) {177		buffer[0] = cpu_to_be64(OWNER_NO_OWNER);178		buffer[1] = cpu_to_be64(179			((u64)device->card->node_id << OWNER_NODE_SHIFT) |180			dice->notification_handler.offset);181 182		dice->owner_generation = device->generation;183		smp_rmb(); /* node_id vs. generation */184		err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,185					 get_subaddr(dice,186						     SND_DICE_ADDR_TYPE_GLOBAL,187						     GLOBAL_OWNER),188					 buffer, 2 * 8,189					 FW_FIXED_GENERATION |190							dice->owner_generation);191		if (err == 0) {192			/* success */193			if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER))194				break;195			/* The address seems to be already registered. */196			if (buffer[0] == buffer[1])197				break;198 199			dev_err(&dice->unit->device,200				"device is already in use\n");201			err = -EBUSY;202		}203		if (err != -EAGAIN || retries-- > 0)204			break;205 206		msleep(20);207	}208 209	kfree(buffer);210 211	if (err < 0)212		dice->owner_generation = -1;213 214	return err;215}216 217static void unregister_notification_address(struct snd_dice *dice)218{219	struct fw_device *device = fw_parent_device(dice->unit);220	__be64 *buffer;221 222	buffer = kmalloc(2 * 8, GFP_KERNEL);223	if (buffer == NULL)224		return;225 226	buffer[0] = cpu_to_be64(227		((u64)device->card->node_id << OWNER_NODE_SHIFT) |228		dice->notification_handler.offset);229	buffer[1] = cpu_to_be64(OWNER_NO_OWNER);230	snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,231			   get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,232				       GLOBAL_OWNER),233			   buffer, 2 * 8, FW_QUIET |234			   FW_FIXED_GENERATION | dice->owner_generation);235 236	kfree(buffer);237 238	dice->owner_generation = -1;239}240 241void snd_dice_transaction_destroy(struct snd_dice *dice)242{243	struct fw_address_handler *handler = &dice->notification_handler;244 245	if (handler->callback_data == NULL)246		return;247 248	unregister_notification_address(dice);249 250	fw_core_remove_address_handler(handler);251	handler->callback_data = NULL;252}253 254int snd_dice_transaction_reinit(struct snd_dice *dice)255{256	struct fw_address_handler *handler = &dice->notification_handler;257 258	if (handler->callback_data == NULL)259		return -EINVAL;260 261	return register_notification_address(dice, false);262}263 264static int get_subaddrs(struct snd_dice *dice)265{266	static const int min_values[10] = {267		10, 0x60 / 4,268		10, 0x18 / 4,269		10, 0x18 / 4,270		0, 0,271		0, 0,272	};273	__be32 *pointers;274	__be32 version;275	u32 data;276	unsigned int i;277	int err;278 279	pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),280				 GFP_KERNEL);281	if (pointers == NULL)282		return -ENOMEM;283 284	/*285	 * Check that the sub address spaces exist and are located inside the286	 * private address space.  The minimum values are chosen so that all287	 * minimally required registers are included.288	 */289	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,290				 DICE_PRIVATE_SPACE, pointers,291				 sizeof(__be32) * ARRAY_SIZE(min_values), 0);292	if (err < 0)293		goto end;294 295	for (i = 0; i < ARRAY_SIZE(min_values); ++i) {296		data = be32_to_cpu(pointers[i]);297		if (data < min_values[i] || data >= 0x40000) {298			err = -ENODEV;299			goto end;300		}301	}302 303	if (be32_to_cpu(pointers[1]) > 0x18) {304		/*305		 * Check that the implemented DICE driver specification major306		 * version number matches.307		 */308		err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,309				DICE_PRIVATE_SPACE +310				be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,311				&version, sizeof(version), 0);312		if (err < 0)313			goto end;314 315		if ((version & cpu_to_be32(0xff000000)) !=316						cpu_to_be32(0x01000000)) {317			dev_err(&dice->unit->device,318				"unknown DICE version: 0x%08x\n",319				be32_to_cpu(version));320			err = -ENODEV;321			goto end;322		}323 324		/* Set up later. */325		dice->clock_caps = 1;326	}327 328	dice->global_offset = be32_to_cpu(pointers[0]) * 4;329	dice->tx_offset = be32_to_cpu(pointers[2]) * 4;330	dice->rx_offset = be32_to_cpu(pointers[4]) * 4;331 332	/* Old firmware doesn't support these fields. */333	if (pointers[7])334		dice->sync_offset = be32_to_cpu(pointers[6]) * 4;335	if (pointers[9])336		dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4;337end:338	kfree(pointers);339	return err;340}341 342int snd_dice_transaction_init(struct snd_dice *dice)343{344	struct fw_address_handler *handler = &dice->notification_handler;345	int err;346 347	err = get_subaddrs(dice);348	if (err < 0)349		return err;350 351	/* Allocation callback in address space over host controller */352	handler->length = 4;353	handler->address_callback = dice_notification;354	handler->callback_data = dice;355	err = fw_core_add_address_handler(handler, &fw_high_memory_region);356	if (err < 0) {357		handler->callback_data = NULL;358		return err;359	}360 361	/* Register the address space */362	err = register_notification_address(dice, true);363	if (err < 0) {364		fw_core_remove_address_handler(handler);365		handler->callback_data = NULL;366	}367 368	return err;369}370