162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * fireworks_transaction.c - a part of driver for Fireworks based devices 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2013-2014 Takashi Sakamoto 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * Fireworks have its own transaction. The transaction can be delivered by AV/C 1062306a36Sopenharmony_ci * Vendor Specific command frame or usual asynchronous transaction. At least, 1162306a36Sopenharmony_ci * Windows driver and firmware version 5.5 or later don't use AV/C command. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Transaction substance: 1462306a36Sopenharmony_ci * At first, 6 data exist. Following to the data, parameters for each command 1562306a36Sopenharmony_ci * exist. All of the parameters are 32 bit aligned to big endian. 1662306a36Sopenharmony_ci * data[0]: Length of transaction substance 1762306a36Sopenharmony_ci * data[1]: Transaction version 1862306a36Sopenharmony_ci * data[2]: Sequence number. This is incremented by the device 1962306a36Sopenharmony_ci * data[3]: Transaction category 2062306a36Sopenharmony_ci * data[4]: Transaction command 2162306a36Sopenharmony_ci * data[5]: Return value in response. 2262306a36Sopenharmony_ci * data[6-]: Parameters 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * Transaction address: 2562306a36Sopenharmony_ci * command: 0xecc000000000 2662306a36Sopenharmony_ci * response: 0xecc080000000 (default) 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * I note that the address for response can be changed by command. But this 2962306a36Sopenharmony_ci * module uses the default address. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci#include "./fireworks.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define MEMORY_SPACE_EFW_COMMAND 0xecc000000000ULL 3462306a36Sopenharmony_ci#define MEMORY_SPACE_EFW_RESPONSE 0xecc080000000ULL 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define ERROR_RETRIES 3 3762306a36Sopenharmony_ci#define ERROR_DELAY_MS 5 3862306a36Sopenharmony_ci#define EFC_TIMEOUT_MS 125 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(instances_lock); 4162306a36Sopenharmony_cistatic struct snd_efw *instances[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(transaction_queues_lock); 4462306a36Sopenharmony_cistatic LIST_HEAD(transaction_queues); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cienum transaction_queue_state { 4762306a36Sopenharmony_ci STATE_PENDING, 4862306a36Sopenharmony_ci STATE_BUS_RESET, 4962306a36Sopenharmony_ci STATE_COMPLETE 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct transaction_queue { 5362306a36Sopenharmony_ci struct list_head list; 5462306a36Sopenharmony_ci struct fw_unit *unit; 5562306a36Sopenharmony_ci void *buf; 5662306a36Sopenharmony_ci unsigned int size; 5762306a36Sopenharmony_ci u32 seqnum; 5862306a36Sopenharmony_ci enum transaction_queue_state state; 5962306a36Sopenharmony_ci wait_queue_head_t wait; 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ciint snd_efw_transaction_cmd(struct fw_unit *unit, 6362306a36Sopenharmony_ci const void *cmd, unsigned int size) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci return snd_fw_transaction(unit, TCODE_WRITE_BLOCK_REQUEST, 6662306a36Sopenharmony_ci MEMORY_SPACE_EFW_COMMAND, 6762306a36Sopenharmony_ci (void *)cmd, size, 0); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ciint snd_efw_transaction_run(struct fw_unit *unit, 7162306a36Sopenharmony_ci const void *cmd, unsigned int cmd_size, 7262306a36Sopenharmony_ci void *resp, unsigned int resp_size) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct transaction_queue t; 7562306a36Sopenharmony_ci unsigned int tries; 7662306a36Sopenharmony_ci int ret; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci t.unit = unit; 7962306a36Sopenharmony_ci t.buf = resp; 8062306a36Sopenharmony_ci t.size = resp_size; 8162306a36Sopenharmony_ci t.seqnum = be32_to_cpu(((struct snd_efw_transaction *)cmd)->seqnum) + 1; 8262306a36Sopenharmony_ci t.state = STATE_PENDING; 8362306a36Sopenharmony_ci init_waitqueue_head(&t.wait); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci spin_lock_irq(&transaction_queues_lock); 8662306a36Sopenharmony_ci list_add_tail(&t.list, &transaction_queues); 8762306a36Sopenharmony_ci spin_unlock_irq(&transaction_queues_lock); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci tries = 0; 9062306a36Sopenharmony_ci do { 9162306a36Sopenharmony_ci ret = snd_efw_transaction_cmd(t.unit, (void *)cmd, cmd_size); 9262306a36Sopenharmony_ci if (ret < 0) 9362306a36Sopenharmony_ci break; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci wait_event_timeout(t.wait, t.state != STATE_PENDING, 9662306a36Sopenharmony_ci msecs_to_jiffies(EFC_TIMEOUT_MS)); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (t.state == STATE_COMPLETE) { 9962306a36Sopenharmony_ci ret = t.size; 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci } else if (t.state == STATE_BUS_RESET) { 10262306a36Sopenharmony_ci msleep(ERROR_DELAY_MS); 10362306a36Sopenharmony_ci } else if (++tries >= ERROR_RETRIES) { 10462306a36Sopenharmony_ci dev_err(&t.unit->device, "EFW transaction timed out\n"); 10562306a36Sopenharmony_ci ret = -EIO; 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci } while (1); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci spin_lock_irq(&transaction_queues_lock); 11162306a36Sopenharmony_ci list_del(&t.list); 11262306a36Sopenharmony_ci spin_unlock_irq(&transaction_queues_lock); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return ret; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void 11862306a36Sopenharmony_cicopy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci size_t capacity, till_end; 12162306a36Sopenharmony_ci struct snd_efw_transaction *t; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci t = (struct snd_efw_transaction *)data; 12462306a36Sopenharmony_ci length = min_t(size_t, be32_to_cpu(t->length) * sizeof(u32), length); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci spin_lock(&efw->lock); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (efw->push_ptr < efw->pull_ptr) 12962306a36Sopenharmony_ci capacity = (unsigned int)(efw->pull_ptr - efw->push_ptr); 13062306a36Sopenharmony_ci else 13162306a36Sopenharmony_ci capacity = snd_efw_resp_buf_size - 13262306a36Sopenharmony_ci (unsigned int)(efw->push_ptr - efw->pull_ptr); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* confirm enough space for this response */ 13562306a36Sopenharmony_ci if (capacity < length) { 13662306a36Sopenharmony_ci *rcode = RCODE_CONFLICT_ERROR; 13762306a36Sopenharmony_ci goto end; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* copy to ring buffer */ 14162306a36Sopenharmony_ci while (length > 0) { 14262306a36Sopenharmony_ci till_end = snd_efw_resp_buf_size - 14362306a36Sopenharmony_ci (unsigned int)(efw->push_ptr - efw->resp_buf); 14462306a36Sopenharmony_ci till_end = min_t(unsigned int, length, till_end); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci memcpy(efw->push_ptr, data, till_end); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci efw->push_ptr += till_end; 14962306a36Sopenharmony_ci if (efw->push_ptr >= efw->resp_buf + snd_efw_resp_buf_size) 15062306a36Sopenharmony_ci efw->push_ptr -= snd_efw_resp_buf_size; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci length -= till_end; 15362306a36Sopenharmony_ci data += till_end; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* for hwdep */ 15762306a36Sopenharmony_ci wake_up(&efw->hwdep_wait); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci *rcode = RCODE_COMPLETE; 16062306a36Sopenharmony_ciend: 16162306a36Sopenharmony_ci spin_unlock_irq(&efw->lock); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void 16562306a36Sopenharmony_cihandle_resp_for_user(struct fw_card *card, int generation, int source, 16662306a36Sopenharmony_ci void *data, size_t length, int *rcode) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct fw_device *device; 16962306a36Sopenharmony_ci struct snd_efw *efw; 17062306a36Sopenharmony_ci unsigned int i; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci spin_lock_irq(&instances_lock); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci for (i = 0; i < SNDRV_CARDS; i++) { 17562306a36Sopenharmony_ci efw = instances[i]; 17662306a36Sopenharmony_ci if (efw == NULL) 17762306a36Sopenharmony_ci continue; 17862306a36Sopenharmony_ci device = fw_parent_device(efw->unit); 17962306a36Sopenharmony_ci if ((device->card != card) || 18062306a36Sopenharmony_ci (device->generation != generation)) 18162306a36Sopenharmony_ci continue; 18262306a36Sopenharmony_ci smp_rmb(); /* node id vs. generation */ 18362306a36Sopenharmony_ci if (device->node_id != source) 18462306a36Sopenharmony_ci continue; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci if (i == SNDRV_CARDS) 18962306a36Sopenharmony_ci goto end; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci copy_resp_to_buf(efw, data, length, rcode); 19262306a36Sopenharmony_ciend: 19362306a36Sopenharmony_ci spin_unlock(&instances_lock); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic void 19762306a36Sopenharmony_cihandle_resp_for_kernel(struct fw_card *card, int generation, int source, 19862306a36Sopenharmony_ci void *data, size_t length, int *rcode, u32 seqnum) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct fw_device *device; 20162306a36Sopenharmony_ci struct transaction_queue *t; 20262306a36Sopenharmony_ci unsigned long flags; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci spin_lock_irqsave(&transaction_queues_lock, flags); 20562306a36Sopenharmony_ci list_for_each_entry(t, &transaction_queues, list) { 20662306a36Sopenharmony_ci device = fw_parent_device(t->unit); 20762306a36Sopenharmony_ci if ((device->card != card) || 20862306a36Sopenharmony_ci (device->generation != generation)) 20962306a36Sopenharmony_ci continue; 21062306a36Sopenharmony_ci smp_rmb(); /* node_id vs. generation */ 21162306a36Sopenharmony_ci if (device->node_id != source) 21262306a36Sopenharmony_ci continue; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if ((t->state == STATE_PENDING) && (t->seqnum == seqnum)) { 21562306a36Sopenharmony_ci t->state = STATE_COMPLETE; 21662306a36Sopenharmony_ci t->size = min_t(unsigned int, length, t->size); 21762306a36Sopenharmony_ci memcpy(t->buf, data, t->size); 21862306a36Sopenharmony_ci wake_up(&t->wait); 21962306a36Sopenharmony_ci *rcode = RCODE_COMPLETE; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci spin_unlock_irqrestore(&transaction_queues_lock, flags); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void 22662306a36Sopenharmony_ciefw_response(struct fw_card *card, struct fw_request *request, 22762306a36Sopenharmony_ci int tcode, int destination, int source, 22862306a36Sopenharmony_ci int generation, unsigned long long offset, 22962306a36Sopenharmony_ci void *data, size_t length, void *callback_data) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci int rcode, dummy; 23262306a36Sopenharmony_ci u32 seqnum; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci rcode = RCODE_TYPE_ERROR; 23562306a36Sopenharmony_ci if (length < sizeof(struct snd_efw_transaction)) { 23662306a36Sopenharmony_ci rcode = RCODE_DATA_ERROR; 23762306a36Sopenharmony_ci goto end; 23862306a36Sopenharmony_ci } else if (offset != MEMORY_SPACE_EFW_RESPONSE) { 23962306a36Sopenharmony_ci rcode = RCODE_ADDRESS_ERROR; 24062306a36Sopenharmony_ci goto end; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum); 24462306a36Sopenharmony_ci if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 1) { 24562306a36Sopenharmony_ci handle_resp_for_kernel(card, generation, source, 24662306a36Sopenharmony_ci data, length, &rcode, seqnum); 24762306a36Sopenharmony_ci if (snd_efw_resp_buf_debug) 24862306a36Sopenharmony_ci handle_resp_for_user(card, generation, source, 24962306a36Sopenharmony_ci data, length, &dummy); 25062306a36Sopenharmony_ci } else { 25162306a36Sopenharmony_ci handle_resp_for_user(card, generation, source, 25262306a36Sopenharmony_ci data, length, &rcode); 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ciend: 25562306a36Sopenharmony_ci fw_send_response(card, request, rcode); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_civoid snd_efw_transaction_add_instance(struct snd_efw *efw) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci unsigned int i; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci spin_lock_irq(&instances_lock); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci for (i = 0; i < SNDRV_CARDS; i++) { 26562306a36Sopenharmony_ci if (instances[i] != NULL) 26662306a36Sopenharmony_ci continue; 26762306a36Sopenharmony_ci instances[i] = efw; 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci spin_unlock_irq(&instances_lock); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_civoid snd_efw_transaction_remove_instance(struct snd_efw *efw) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci unsigned int i; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci spin_lock_irq(&instances_lock); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci for (i = 0; i < SNDRV_CARDS; i++) { 28162306a36Sopenharmony_ci if (instances[i] != efw) 28262306a36Sopenharmony_ci continue; 28362306a36Sopenharmony_ci instances[i] = NULL; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci spin_unlock_irq(&instances_lock); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_civoid snd_efw_transaction_bus_reset(struct fw_unit *unit) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct transaction_queue *t; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci spin_lock_irq(&transaction_queues_lock); 29462306a36Sopenharmony_ci list_for_each_entry(t, &transaction_queues, list) { 29562306a36Sopenharmony_ci if ((t->unit == unit) && 29662306a36Sopenharmony_ci (t->state == STATE_PENDING)) { 29762306a36Sopenharmony_ci t->state = STATE_BUS_RESET; 29862306a36Sopenharmony_ci wake_up(&t->wait); 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci spin_unlock_irq(&transaction_queues_lock); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic struct fw_address_handler resp_register_handler = { 30562306a36Sopenharmony_ci .length = SND_EFW_RESPONSE_MAXIMUM_BYTES, 30662306a36Sopenharmony_ci .address_callback = efw_response 30762306a36Sopenharmony_ci}; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ciint snd_efw_transaction_register(void) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci static const struct fw_address_region resp_register_region = { 31262306a36Sopenharmony_ci .start = MEMORY_SPACE_EFW_RESPONSE, 31362306a36Sopenharmony_ci .end = MEMORY_SPACE_EFW_RESPONSE + 31462306a36Sopenharmony_ci SND_EFW_RESPONSE_MAXIMUM_BYTES 31562306a36Sopenharmony_ci }; 31662306a36Sopenharmony_ci return fw_core_add_address_handler(&resp_register_handler, 31762306a36Sopenharmony_ci &resp_register_region); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_civoid snd_efw_transaction_unregister(void) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci WARN_ON(!list_empty(&transaction_queues)); 32362306a36Sopenharmony_ci fw_core_remove_address_handler(&resp_register_handler); 32462306a36Sopenharmony_ci} 325