162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * digi00x-transaction.c - a part of driver for Digidesign Digi 002/003 family
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2014-2015 Takashi Sakamoto
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <sound/asound.h>
962306a36Sopenharmony_ci#include "digi00x.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic void handle_unknown_message(struct snd_dg00x *dg00x,
1262306a36Sopenharmony_ci				   unsigned long long offset, __be32 *buf)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	unsigned long flags;
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci	spin_lock_irqsave(&dg00x->lock, flags);
1762306a36Sopenharmony_ci	dg00x->msg = be32_to_cpu(*buf);
1862306a36Sopenharmony_ci	spin_unlock_irqrestore(&dg00x->lock, flags);
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	wake_up(&dg00x->hwdep_wait);
2162306a36Sopenharmony_ci}
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic void handle_message(struct fw_card *card, struct fw_request *request,
2462306a36Sopenharmony_ci			   int tcode, int destination, int source,
2562306a36Sopenharmony_ci			   int generation, unsigned long long offset,
2662306a36Sopenharmony_ci			   void *data, size_t length, void *callback_data)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	struct snd_dg00x *dg00x = callback_data;
2962306a36Sopenharmony_ci	__be32 *buf = (__be32 *)data;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	fw_send_response(card, request, RCODE_COMPLETE);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	if (offset == dg00x->async_handler.offset)
3462306a36Sopenharmony_ci		handle_unknown_message(dg00x, offset, buf);
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ciint snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct fw_device *device = fw_parent_device(dg00x->unit);
4062306a36Sopenharmony_ci	__be32 data[2];
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	/* Unknown. 4bytes. */
4362306a36Sopenharmony_ci	data[0] = cpu_to_be32((device->card->node_id << 16) |
4462306a36Sopenharmony_ci			      (dg00x->async_handler.offset >> 32));
4562306a36Sopenharmony_ci	data[1] = cpu_to_be32(dg00x->async_handler.offset);
4662306a36Sopenharmony_ci	return snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
4762306a36Sopenharmony_ci				  DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR,
4862306a36Sopenharmony_ci				  &data, sizeof(data), 0);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_civoid snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	if (dg00x->async_handler.callback_data == NULL)
5462306a36Sopenharmony_ci		return;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	fw_core_remove_address_handler(&dg00x->async_handler);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	dg00x->async_handler.callback_data = NULL;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ciint snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	static const struct fw_address_region resp_register_region = {
6462306a36Sopenharmony_ci		.start	= 0xffffe0000000ull,
6562306a36Sopenharmony_ci		.end	= 0xffffe000ffffull,
6662306a36Sopenharmony_ci	};
6762306a36Sopenharmony_ci	int err;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	dg00x->async_handler.length = 4;
7062306a36Sopenharmony_ci	dg00x->async_handler.address_callback = handle_message;
7162306a36Sopenharmony_ci	dg00x->async_handler.callback_data = dg00x;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	err = fw_core_add_address_handler(&dg00x->async_handler,
7462306a36Sopenharmony_ci					  &resp_register_region);
7562306a36Sopenharmony_ci	if (err < 0)
7662306a36Sopenharmony_ci		return err;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	err = snd_dg00x_transaction_reregister(dg00x);
7962306a36Sopenharmony_ci	if (err < 0)
8062306a36Sopenharmony_ci		snd_dg00x_transaction_unregister(dg00x);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	return err;
8362306a36Sopenharmony_ci}
84