18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Connection Management Procedures (IEC 61883-1) helper functions 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/device.h> 98c2ecf20Sopenharmony_ci#include <linux/firewire.h> 108c2ecf20Sopenharmony_ci#include <linux/firewire-constants.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/sched.h> 138c2ecf20Sopenharmony_ci#include "lib.h" 148c2ecf20Sopenharmony_ci#include "iso-resources.h" 158c2ecf20Sopenharmony_ci#include "cmp.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* MPR common fields */ 188c2ecf20Sopenharmony_ci#define MPR_SPEED_MASK 0xc0000000 198c2ecf20Sopenharmony_ci#define MPR_SPEED_SHIFT 30 208c2ecf20Sopenharmony_ci#define MPR_XSPEED_MASK 0x00000060 218c2ecf20Sopenharmony_ci#define MPR_XSPEED_SHIFT 5 228c2ecf20Sopenharmony_ci#define MPR_PLUGS_MASK 0x0000001f 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* PCR common fields */ 258c2ecf20Sopenharmony_ci#define PCR_ONLINE 0x80000000 268c2ecf20Sopenharmony_ci#define PCR_BCAST_CONN 0x40000000 278c2ecf20Sopenharmony_ci#define PCR_P2P_CONN_MASK 0x3f000000 288c2ecf20Sopenharmony_ci#define PCR_P2P_CONN_SHIFT 24 298c2ecf20Sopenharmony_ci#define PCR_CHANNEL_MASK 0x003f0000 308c2ecf20Sopenharmony_ci#define PCR_CHANNEL_SHIFT 16 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* oPCR specific fields */ 338c2ecf20Sopenharmony_ci#define OPCR_XSPEED_MASK 0x00C00000 348c2ecf20Sopenharmony_ci#define OPCR_XSPEED_SHIFT 22 358c2ecf20Sopenharmony_ci#define OPCR_SPEED_MASK 0x0000C000 368c2ecf20Sopenharmony_ci#define OPCR_SPEED_SHIFT 14 378c2ecf20Sopenharmony_ci#define OPCR_OVERHEAD_ID_MASK 0x00003C00 388c2ecf20Sopenharmony_ci#define OPCR_OVERHEAD_ID_SHIFT 10 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cienum bus_reset_handling { 418c2ecf20Sopenharmony_ci ABORT_ON_BUS_RESET, 428c2ecf20Sopenharmony_ci SUCCEED_ON_BUS_RESET, 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic __printf(2, 3) 468c2ecf20Sopenharmony_civoid cmp_error(struct cmp_connection *c, const char *fmt, ...) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci va_list va; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci va_start(va, fmt); 518c2ecf20Sopenharmony_ci dev_err(&c->resources.unit->device, "%cPCR%u: %pV", 528c2ecf20Sopenharmony_ci (c->direction == CMP_INPUT) ? 'i' : 'o', 538c2ecf20Sopenharmony_ci c->pcr_index, &(struct va_format){ fmt, &va }); 548c2ecf20Sopenharmony_ci va_end(va); 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic u64 mpr_address(struct cmp_connection *c) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci if (c->direction == CMP_INPUT) 608c2ecf20Sopenharmony_ci return CSR_REGISTER_BASE + CSR_IMPR; 618c2ecf20Sopenharmony_ci else 628c2ecf20Sopenharmony_ci return CSR_REGISTER_BASE + CSR_OMPR; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic u64 pcr_address(struct cmp_connection *c) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci if (c->direction == CMP_INPUT) 688c2ecf20Sopenharmony_ci return CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index); 698c2ecf20Sopenharmony_ci else 708c2ecf20Sopenharmony_ci return CSR_REGISTER_BASE + CSR_OPCR(c->pcr_index); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int pcr_modify(struct cmp_connection *c, 748c2ecf20Sopenharmony_ci __be32 (*modify)(struct cmp_connection *c, __be32 old), 758c2ecf20Sopenharmony_ci int (*check)(struct cmp_connection *c, __be32 pcr), 768c2ecf20Sopenharmony_ci enum bus_reset_handling bus_reset_handling) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci __be32 old_arg, buffer[2]; 798c2ecf20Sopenharmony_ci int err; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci buffer[0] = c->last_pcr_value; 828c2ecf20Sopenharmony_ci for (;;) { 838c2ecf20Sopenharmony_ci old_arg = buffer[0]; 848c2ecf20Sopenharmony_ci buffer[1] = modify(c, buffer[0]); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci err = snd_fw_transaction( 878c2ecf20Sopenharmony_ci c->resources.unit, TCODE_LOCK_COMPARE_SWAP, 888c2ecf20Sopenharmony_ci pcr_address(c), buffer, 8, 898c2ecf20Sopenharmony_ci FW_FIXED_GENERATION | c->resources.generation); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (err < 0) { 928c2ecf20Sopenharmony_ci if (err == -EAGAIN && 938c2ecf20Sopenharmony_ci bus_reset_handling == SUCCEED_ON_BUS_RESET) 948c2ecf20Sopenharmony_ci err = 0; 958c2ecf20Sopenharmony_ci return err; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (buffer[0] == old_arg) /* success? */ 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (check) { 1028c2ecf20Sopenharmony_ci err = check(c, buffer[0]); 1038c2ecf20Sopenharmony_ci if (err < 0) 1048c2ecf20Sopenharmony_ci return err; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci c->last_pcr_value = buffer[1]; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/** 1148c2ecf20Sopenharmony_ci * cmp_connection_init - initializes a connection manager 1158c2ecf20Sopenharmony_ci * @c: the connection manager to initialize 1168c2ecf20Sopenharmony_ci * @unit: a unit of the target device 1178c2ecf20Sopenharmony_ci * @direction: input or output 1188c2ecf20Sopenharmony_ci * @pcr_index: the index of the iPCR/oPCR on the target device 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_ciint cmp_connection_init(struct cmp_connection *c, 1218c2ecf20Sopenharmony_ci struct fw_unit *unit, 1228c2ecf20Sopenharmony_ci enum cmp_direction direction, 1238c2ecf20Sopenharmony_ci unsigned int pcr_index) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci __be32 mpr_be; 1268c2ecf20Sopenharmony_ci u32 mpr; 1278c2ecf20Sopenharmony_ci int err; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci c->direction = direction; 1308c2ecf20Sopenharmony_ci err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, 1318c2ecf20Sopenharmony_ci mpr_address(c), &mpr_be, 4, 0); 1328c2ecf20Sopenharmony_ci if (err < 0) 1338c2ecf20Sopenharmony_ci return err; 1348c2ecf20Sopenharmony_ci mpr = be32_to_cpu(mpr_be); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (pcr_index >= (mpr & MPR_PLUGS_MASK)) 1378c2ecf20Sopenharmony_ci return -EINVAL; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci err = fw_iso_resources_init(&c->resources, unit); 1408c2ecf20Sopenharmony_ci if (err < 0) 1418c2ecf20Sopenharmony_ci return err; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci c->connected = false; 1448c2ecf20Sopenharmony_ci mutex_init(&c->mutex); 1458c2ecf20Sopenharmony_ci c->last_pcr_value = cpu_to_be32(0x80000000); 1468c2ecf20Sopenharmony_ci c->pcr_index = pcr_index; 1478c2ecf20Sopenharmony_ci c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT; 1488c2ecf20Sopenharmony_ci if (c->max_speed == SCODE_BETA) 1498c2ecf20Sopenharmony_ci c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmp_connection_init); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/** 1568c2ecf20Sopenharmony_ci * cmp_connection_check_used - check connection is already esablished or not 1578c2ecf20Sopenharmony_ci * @c: the connection manager to be checked 1588c2ecf20Sopenharmony_ci * @used: the pointer to store the result of checking the connection 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ciint cmp_connection_check_used(struct cmp_connection *c, bool *used) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci __be32 pcr; 1638c2ecf20Sopenharmony_ci int err; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci err = snd_fw_transaction( 1668c2ecf20Sopenharmony_ci c->resources.unit, TCODE_READ_QUADLET_REQUEST, 1678c2ecf20Sopenharmony_ci pcr_address(c), &pcr, 4, 0); 1688c2ecf20Sopenharmony_ci if (err >= 0) 1698c2ecf20Sopenharmony_ci *used = !!(pcr & cpu_to_be32(PCR_BCAST_CONN | 1708c2ecf20Sopenharmony_ci PCR_P2P_CONN_MASK)); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return err; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmp_connection_check_used); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/** 1778c2ecf20Sopenharmony_ci * cmp_connection_destroy - free connection manager resources 1788c2ecf20Sopenharmony_ci * @c: the connection manager 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_civoid cmp_connection_destroy(struct cmp_connection *c) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci WARN_ON(c->connected); 1838c2ecf20Sopenharmony_ci mutex_destroy(&c->mutex); 1848c2ecf20Sopenharmony_ci fw_iso_resources_destroy(&c->resources); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmp_connection_destroy); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ciint cmp_connection_reserve(struct cmp_connection *c, 1898c2ecf20Sopenharmony_ci unsigned int max_payload_bytes) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci int err; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci mutex_lock(&c->mutex); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (WARN_ON(c->resources.allocated)) { 1968c2ecf20Sopenharmony_ci err = -EBUSY; 1978c2ecf20Sopenharmony_ci goto end; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci c->speed = min(c->max_speed, 2018c2ecf20Sopenharmony_ci fw_parent_device(c->resources.unit)->max_speed); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci err = fw_iso_resources_allocate(&c->resources, max_payload_bytes, 2048c2ecf20Sopenharmony_ci c->speed); 2058c2ecf20Sopenharmony_ciend: 2068c2ecf20Sopenharmony_ci mutex_unlock(&c->mutex); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return err; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmp_connection_reserve); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_civoid cmp_connection_release(struct cmp_connection *c) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci mutex_lock(&c->mutex); 2158c2ecf20Sopenharmony_ci fw_iso_resources_free(&c->resources); 2168c2ecf20Sopenharmony_ci mutex_unlock(&c->mutex); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmp_connection_release); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci ipcr &= ~cpu_to_be32(PCR_BCAST_CONN | 2238c2ecf20Sopenharmony_ci PCR_P2P_CONN_MASK | 2248c2ecf20Sopenharmony_ci PCR_CHANNEL_MASK); 2258c2ecf20Sopenharmony_ci ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT); 2268c2ecf20Sopenharmony_ci ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return ipcr; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic int get_overhead_id(struct cmp_connection *c) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci int id; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* 2368c2ecf20Sopenharmony_ci * apply "oPCR overhead ID encoding" 2378c2ecf20Sopenharmony_ci * the encoding table can convert up to 512. 2388c2ecf20Sopenharmony_ci * here the value over 512 is converted as the same way as 512. 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_ci for (id = 1; id < 16; id++) { 2418c2ecf20Sopenharmony_ci if (c->resources.bandwidth_overhead < (id << 5)) 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci if (id == 16) 2458c2ecf20Sopenharmony_ci id = 0; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return id; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci unsigned int spd, xspd; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* generate speed and extended speed field value */ 2558c2ecf20Sopenharmony_ci if (c->speed > SCODE_400) { 2568c2ecf20Sopenharmony_ci spd = SCODE_800; 2578c2ecf20Sopenharmony_ci xspd = c->speed - SCODE_800; 2588c2ecf20Sopenharmony_ci } else { 2598c2ecf20Sopenharmony_ci spd = c->speed; 2608c2ecf20Sopenharmony_ci xspd = 0; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci opcr &= ~cpu_to_be32(PCR_BCAST_CONN | 2648c2ecf20Sopenharmony_ci PCR_P2P_CONN_MASK | 2658c2ecf20Sopenharmony_ci OPCR_XSPEED_MASK | 2668c2ecf20Sopenharmony_ci PCR_CHANNEL_MASK | 2678c2ecf20Sopenharmony_ci OPCR_SPEED_MASK | 2688c2ecf20Sopenharmony_ci OPCR_OVERHEAD_ID_MASK); 2698c2ecf20Sopenharmony_ci opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT); 2708c2ecf20Sopenharmony_ci opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT); 2718c2ecf20Sopenharmony_ci opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT); 2728c2ecf20Sopenharmony_ci opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT); 2738c2ecf20Sopenharmony_ci opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return opcr; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int pcr_set_check(struct cmp_connection *c, __be32 pcr) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci if (pcr & cpu_to_be32(PCR_BCAST_CONN | 2818c2ecf20Sopenharmony_ci PCR_P2P_CONN_MASK)) { 2828c2ecf20Sopenharmony_ci cmp_error(c, "plug is already in use\n"); 2838c2ecf20Sopenharmony_ci return -EBUSY; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci if (!(pcr & cpu_to_be32(PCR_ONLINE))) { 2868c2ecf20Sopenharmony_ci cmp_error(c, "plug is not on-line\n"); 2878c2ecf20Sopenharmony_ci return -ECONNREFUSED; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci/** 2948c2ecf20Sopenharmony_ci * cmp_connection_establish - establish a connection to the target 2958c2ecf20Sopenharmony_ci * @c: the connection manager 2968c2ecf20Sopenharmony_ci * 2978c2ecf20Sopenharmony_ci * This function establishes a point-to-point connection from the local 2988c2ecf20Sopenharmony_ci * computer to the target by allocating isochronous resources (channel and 2998c2ecf20Sopenharmony_ci * bandwidth) and setting the target's input/output plug control register. 3008c2ecf20Sopenharmony_ci * When this function succeeds, the caller is responsible for starting 3018c2ecf20Sopenharmony_ci * transmitting packets. 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_ciint cmp_connection_establish(struct cmp_connection *c) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci int err; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci mutex_lock(&c->mutex); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (WARN_ON(c->connected)) { 3108c2ecf20Sopenharmony_ci mutex_unlock(&c->mutex); 3118c2ecf20Sopenharmony_ci return -EISCONN; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ciretry_after_bus_reset: 3158c2ecf20Sopenharmony_ci if (c->direction == CMP_OUTPUT) 3168c2ecf20Sopenharmony_ci err = pcr_modify(c, opcr_set_modify, pcr_set_check, 3178c2ecf20Sopenharmony_ci ABORT_ON_BUS_RESET); 3188c2ecf20Sopenharmony_ci else 3198c2ecf20Sopenharmony_ci err = pcr_modify(c, ipcr_set_modify, pcr_set_check, 3208c2ecf20Sopenharmony_ci ABORT_ON_BUS_RESET); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (err == -EAGAIN) { 3238c2ecf20Sopenharmony_ci err = fw_iso_resources_update(&c->resources); 3248c2ecf20Sopenharmony_ci if (err >= 0) 3258c2ecf20Sopenharmony_ci goto retry_after_bus_reset; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci if (err >= 0) 3288c2ecf20Sopenharmony_ci c->connected = true; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci mutex_unlock(&c->mutex); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return err; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmp_connection_establish); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci/** 3378c2ecf20Sopenharmony_ci * cmp_connection_update - update the connection after a bus reset 3388c2ecf20Sopenharmony_ci * @c: the connection manager 3398c2ecf20Sopenharmony_ci * 3408c2ecf20Sopenharmony_ci * This function must be called from the driver's .update handler to 3418c2ecf20Sopenharmony_ci * reestablish any connection that might have been active. 3428c2ecf20Sopenharmony_ci * 3438c2ecf20Sopenharmony_ci * Returns zero on success, or a negative error code. On an error, the 3448c2ecf20Sopenharmony_ci * connection is broken and the caller must stop transmitting iso packets. 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_ciint cmp_connection_update(struct cmp_connection *c) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci int err; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci mutex_lock(&c->mutex); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (!c->connected) { 3538c2ecf20Sopenharmony_ci mutex_unlock(&c->mutex); 3548c2ecf20Sopenharmony_ci return 0; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci err = fw_iso_resources_update(&c->resources); 3588c2ecf20Sopenharmony_ci if (err < 0) 3598c2ecf20Sopenharmony_ci goto err_unconnect; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (c->direction == CMP_OUTPUT) 3628c2ecf20Sopenharmony_ci err = pcr_modify(c, opcr_set_modify, pcr_set_check, 3638c2ecf20Sopenharmony_ci SUCCEED_ON_BUS_RESET); 3648c2ecf20Sopenharmony_ci else 3658c2ecf20Sopenharmony_ci err = pcr_modify(c, ipcr_set_modify, pcr_set_check, 3668c2ecf20Sopenharmony_ci SUCCEED_ON_BUS_RESET); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (err < 0) 3698c2ecf20Sopenharmony_ci goto err_unconnect; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci mutex_unlock(&c->mutex); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci return 0; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cierr_unconnect: 3768c2ecf20Sopenharmony_ci c->connected = false; 3778c2ecf20Sopenharmony_ci mutex_unlock(&c->mutex); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return err; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmp_connection_update); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK); 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci/** 3898c2ecf20Sopenharmony_ci * cmp_connection_break - break the connection to the target 3908c2ecf20Sopenharmony_ci * @c: the connection manager 3918c2ecf20Sopenharmony_ci * 3928c2ecf20Sopenharmony_ci * This function deactives the connection in the target's input/output plug 3938c2ecf20Sopenharmony_ci * control register, and frees the isochronous resources of the connection. 3948c2ecf20Sopenharmony_ci * Before calling this function, the caller should cease transmitting packets. 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_civoid cmp_connection_break(struct cmp_connection *c) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci int err; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci mutex_lock(&c->mutex); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (!c->connected) { 4038c2ecf20Sopenharmony_ci mutex_unlock(&c->mutex); 4048c2ecf20Sopenharmony_ci return; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET); 4088c2ecf20Sopenharmony_ci if (err < 0) 4098c2ecf20Sopenharmony_ci cmp_error(c, "plug is still connected\n"); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci c->connected = false; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci mutex_unlock(&c->mutex); 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cmp_connection_break); 416