File Index
Files and directories
Sign in
FILE

vivid-cec.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/test-drivers/vivid/vivid-cec.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * vivid-cec.c - A Virtual Video Test Driver, cec emulation
  4 *
  5 * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  6 */
  7
  8#include <linux/delay.h>
  9#include <media/cec.h>
 10
 11#include "vivid-core.h"
 12#include "vivid-cec.h"
 13
 14#define CEC_START_BIT_US		4500
 15#define CEC_DATA_BIT_US			2400
 16#define CEC_MARGIN_US			350
 17
 18struct xfer_on_bus {
 19	struct cec_adapter	*adap;
 20	u8			status;
 21};
 22
 23static bool find_dest_adap(struct vivid_dev *dev,
 24			   struct cec_adapter *adap, u8 dest)
 25{
 26	unsigned int i, j;
 27
 28	if (dest >= 0xf)
 29		return false;
 30
 31	if (adap != dev->cec_rx_adap && dev->cec_rx_adap &&
 32	    dev->cec_rx_adap->is_configured &&
 33	    cec_has_log_addr(dev->cec_rx_adap, dest))
 34		return true;
 35
 36	for (i = 0, j = 0; i < dev->num_inputs; i++) {
 37		unsigned int menu_idx =
 38			dev->input_is_connected_to_output[i];
 39
 40		if (dev->input_type[i] != HDMI)
 41			continue;
 42		j++;
 43		if (menu_idx < FIXED_MENU_ITEMS)
 44			continue;
 45
 46		struct vivid_dev *dev_tx = vivid_ctrl_hdmi_to_output_instance[menu_idx];
 47		unsigned int output = vivid_ctrl_hdmi_to_output_index[menu_idx];
 48
 49		if (!dev_tx)
 50			continue;
 51
 52		unsigned int hdmi_output = dev_tx->output_to_iface_index[output];
 53
 54		if (adap == dev_tx->cec_tx_adap[hdmi_output])
 55			continue;
 56		if (!dev_tx->cec_tx_adap[hdmi_output]->is_configured)
 57			continue;
 58		if (cec_has_log_addr(dev_tx->cec_tx_adap[hdmi_output], dest))
 59			return true;
 60	}
 61	return false;
 62}
 63
 64static bool xfer_ready(struct vivid_dev *dev)
 65{
 66	unsigned int i;
 67	bool ready = false;
 68
 69	spin_lock(&dev->cec_xfers_slock);
 70	for (i = 0; i < ARRAY_SIZE(dev->xfers); i++) {
 71		if (dev->xfers[i].sft &&
 72		    dev->xfers[i].sft <= dev->cec_sft) {
 73			ready = true;
 74			break;
 75		}
 76	}
 77	spin_unlock(&dev->cec_xfers_slock);
 78
 79	return ready;
 80}
 81
 82/*
 83 * If an adapter tries to send successive messages, it must wait for the
 84 * longest signal-free time between its transmissions. But, if another
 85 * adapter sends a message in the interim, then the wait can be reduced
 86 * because the messages are no longer successive. Make these adjustments
 87 * if necessary. Should be called holding cec_xfers_slock.
 88 */
 89static void adjust_sfts(struct vivid_dev *dev)
 90{
 91	unsigned int i;
 92	u8 initiator;
 93
 94	for (i = 0; i < ARRAY_SIZE(dev->xfers); i++) {
 95		if (dev->xfers[i].sft <= CEC_SIGNAL_FREE_TIME_RETRY)
 96			continue;
 97		initiator = dev->xfers[i].msg[0] >> 4;
 98		if (initiator == dev->last_initiator)
 99			dev->xfers[i].sft = CEC_SIGNAL_FREE_TIME_NEXT_XFER;
100		else
101			dev->xfers[i].sft = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
102	}
103}
104
105/*
106 * The main emulation of the bus on which CEC adapters attempt to send
107 * messages to each other. The bus keeps track of how long it has been
108 * signal-free and accepts a pending transmission only if the state of
109 * the bus matches the transmission's signal-free requirements. It calls
110 * cec_transmit_attempt_done() for all transmits that enter the bus and
111 * cec_received_msg() for successful transmits.
112 */
113int vivid_cec_bus_thread(void *_dev)
114{
115	u32 last_sft;
116	unsigned int i, j;
117	unsigned int dest;
118	ktime_t start, end;
119	s64 delta_us, retry_us;
120	struct vivid_dev *dev = _dev;
121
122	dev->cec_sft = CEC_SIGNAL_FREE_TIME_NEXT_XFER;
123	for (;;) {
124		bool first = true;
125		int wait_xfer_us = 0;
126		bool valid_dest = false;
127		int wait_arb_lost_us = 0;
128		unsigned int first_idx = 0;
129		unsigned int first_status = 0;
130		struct cec_msg first_msg = {};
131		struct xfer_on_bus xfers_on_bus[MAX_OUTPUTS] = {};
132
133		wait_event_interruptible(dev->kthread_waitq_cec, xfer_ready(dev) ||
134					 kthread_should_stop());
135		if (kthread_should_stop())
136			break;
137		last_sft = dev->cec_sft;
138		dev->cec_sft = 0;
139		/*
140		 * Move the messages that are ready onto the bus. The adapter with
141		 * the most leading zeros will win control of the bus and any other
142		 * adapters will lose arbitration.
143		 */
144		spin_lock(&dev->cec_xfers_slock);
145		for (i = 0; i < ARRAY_SIZE(dev->xfers); i++) {
146			if (!dev->xfers[i].sft || dev->xfers[i].sft > last_sft)
147				continue;
148			if (first) {
149				first = false;
150				first_idx = i;
151				xfers_on_bus[first_idx].adap = dev->xfers[i].adap;
152				memcpy(first_msg.msg, dev->xfers[i].msg, dev->xfers[i].len);
153				first_msg.len = dev->xfers[i].len;
154			} else {
155				xfers_on_bus[i].adap = dev->xfers[i].adap;
156				xfers_on_bus[i].status = CEC_TX_STATUS_ARB_LOST;
157				/*
158				 * For simplicity wait for all 4 bits of the initiator's
159				 * address even though HDMI specification uses bit-level
160				 * precision.
161				 */
162				wait_arb_lost_us = 4 * CEC_DATA_BIT_US + CEC_START_BIT_US;
163			}
164			dev->xfers[i].sft = 0;
165		}
166		dev->last_initiator = cec_msg_initiator(&first_msg);
167		adjust_sfts(dev);
168		spin_unlock(&dev->cec_xfers_slock);
169
170		dest = cec_msg_destination(&first_msg);
171		valid_dest = cec_msg_is_broadcast(&first_msg);
172		if (!valid_dest)
173			valid_dest = find_dest_adap(dev, xfers_on_bus[first_idx].adap, dest);
174		if (valid_dest) {
175			first_status = CEC_TX_STATUS_OK;
176			/*
177			 * Message length is in bytes, but each byte is transmitted in
178			 * a block of 10 bits.
179			 */
180			wait_xfer_us = first_msg.len * 10 * CEC_DATA_BIT_US;
181		} else {
182			first_status = CEC_TX_STATUS_NACK;
183			/*
184			 * A message that is not acknowledged stops transmitting after
185			 * the header block of 10 bits.
186			 */
187			wait_xfer_us = 10 * CEC_DATA_BIT_US;
188		}
189		wait_xfer_us += CEC_START_BIT_US;
190		xfers_on_bus[first_idx].status = first_status;
191
192		/* Sleep as if sending messages on a real hardware bus. */
193		start = ktime_get();
194		if (wait_arb_lost_us) {
195			usleep_range(wait_arb_lost_us - CEC_MARGIN_US, wait_arb_lost_us);
196			for (i = 0; i < ARRAY_SIZE(xfers_on_bus); i++) {
197				if (xfers_on_bus[i].status != CEC_TX_STATUS_ARB_LOST)
198					continue;
199				cec_transmit_attempt_done(xfers_on_bus[i].adap,
200							  CEC_TX_STATUS_ARB_LOST);
201			}
202			if (kthread_should_stop())
203				break;
204		}
205		wait_xfer_us -= wait_arb_lost_us;
206		usleep_range(wait_xfer_us - CEC_MARGIN_US, wait_xfer_us);
207		cec_transmit_attempt_done(xfers_on_bus[first_idx].adap, first_status);
208		if (kthread_should_stop())
209			break;
210		if (first_status == CEC_TX_STATUS_OK) {
211			if (xfers_on_bus[first_idx].adap != dev->cec_rx_adap)
212				cec_received_msg(dev->cec_rx_adap, &first_msg);
213			for (i = 0, j = 0; i < dev->num_inputs; i++) {
214				unsigned int menu_idx =
215					dev->input_is_connected_to_output[i];
216
217				if (dev->input_type[i] != HDMI)
218					continue;
219				j++;
220				if (menu_idx < FIXED_MENU_ITEMS)
221					continue;
222
223				struct vivid_dev *dev_tx = vivid_ctrl_hdmi_to_output_instance[menu_idx];
224				unsigned int output = vivid_ctrl_hdmi_to_output_index[menu_idx];
225
226				if (!dev_tx)
227					continue;
228
229				unsigned int hdmi_output = dev_tx->output_to_iface_index[output];
230
231				if (xfers_on_bus[first_idx].adap != dev_tx->cec_tx_adap[hdmi_output])
232					cec_received_msg(dev_tx->cec_tx_adap[hdmi_output], &first_msg);
233			}
234		}
235		end = ktime_get();
236		/*
237		 * If the emulated transfer took more or less time than it should
238		 * have, then compensate by adjusting the wait time needed for the
239		 * bus to be signal-free for 3 bit periods (the retry time).
240		 */
241		delta_us = div_s64(end - start, 1000);
242		delta_us -= wait_xfer_us + wait_arb_lost_us;
243		retry_us = CEC_SIGNAL_FREE_TIME_RETRY * CEC_DATA_BIT_US - delta_us;
244		if (retry_us > CEC_MARGIN_US)
245			usleep_range(retry_us - CEC_MARGIN_US, retry_us);
246		dev->cec_sft = CEC_SIGNAL_FREE_TIME_RETRY;
247		/*
248		 * If there are no messages that need to be retried, check if any
249		 * adapters that did not just transmit a message are ready to
250		 * transmit. If none of these adapters are ready, then increase
251		 * the signal-free time so that the bus is available to all
252		 * adapters and go back to waiting for a transmission.
253		 */
254		while (dev->cec_sft >= CEC_SIGNAL_FREE_TIME_RETRY &&
255		       dev->cec_sft < CEC_SIGNAL_FREE_TIME_NEXT_XFER &&
256		       !xfer_ready(dev) && !kthread_should_stop()) {
257			usleep_range(2 * CEC_DATA_BIT_US - CEC_MARGIN_US,
258				     2 * CEC_DATA_BIT_US);
259			dev->cec_sft += 2;
260		}
261	}
262	return 0;
263}
264
265static int vivid_cec_adap_enable(struct cec_adapter *adap, bool enable)
266{
267	adap->cec_pin_is_high = true;
268	return 0;
269}
270
271static int vivid_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
272{
273	return 0;
274}
275
276static int vivid_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
277				   u32 signal_free_time, struct cec_msg *msg)
278{
279	struct vivid_dev *dev = cec_get_drvdata(adap);
280	struct vivid_dev *dev_rx = dev;
281	u8 idx = cec_msg_initiator(msg);
282	u8 output = 0;
283
284	if (dev->cec_rx_adap != adap) {
285		int i;
286
287		for (i = 0; i < dev->num_hdmi_outputs; i++)
288			if (dev->cec_tx_adap[i] == adap)
289				break;
290		if (i == dev->num_hdmi_outputs)
291			return -ENONET;
292		output = dev->hdmi_index_to_output_index[i];
293		dev_rx = dev->output_to_input_instance[output];
294		if (!dev_rx)
295			return -ENONET;
296	}
297	spin_lock(&dev_rx->cec_xfers_slock);
298	dev_rx->xfers[idx].adap = adap;
299	memcpy(dev_rx->xfers[idx].msg, msg->msg, CEC_MAX_MSG_SIZE);
300	dev_rx->xfers[idx].len = msg->len;
301	dev_rx->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_RETRY;
302	if (signal_free_time > CEC_SIGNAL_FREE_TIME_RETRY) {
303		if (idx == dev_rx->last_initiator)
304			dev_rx->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEXT_XFER;
305		else
306			dev_rx->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
307	}
308	spin_unlock(&dev_rx->cec_xfers_slock);
309	wake_up_interruptible(&dev_rx->kthread_waitq_cec);
310
311	return 0;
312}
313
314static int vivid_received(struct cec_adapter *adap, struct cec_msg *msg)
315{
316	struct vivid_dev *dev = cec_get_drvdata(adap);
317	struct cec_msg reply;
318	u8 dest = cec_msg_destination(msg);
319
320	if (cec_msg_is_broadcast(msg))
321		dest = adap->log_addrs.log_addr[0];
322	cec_msg_init(&reply, dest, cec_msg_initiator(msg));
323
324	switch (cec_msg_opcode(msg)) {
325	case CEC_MSG_SET_OSD_STRING: {
326		u8 disp_ctl;
327		char osd[14];
328
329		if (!cec_is_sink(adap))
330			break;
331		cec_ops_set_osd_string(msg, &disp_ctl, osd);
332		switch (disp_ctl) {
333		case CEC_OP_DISP_CTL_DEFAULT:
334			strscpy(dev->osd, osd, sizeof(dev->osd));
335			dev->osd_jiffies = jiffies;
336			break;
337		case CEC_OP_DISP_CTL_UNTIL_CLEARED:
338			strscpy(dev->osd, osd, sizeof(dev->osd));
339			dev->osd_jiffies = 0;
340			break;
341		case CEC_OP_DISP_CTL_CLEAR:
342			dev->osd[0] = 0;
343			dev->osd_jiffies = 0;
344			break;
345		default:
346			cec_msg_feature_abort(&reply, cec_msg_opcode(msg),
347					      CEC_OP_ABORT_INVALID_OP);
348			cec_transmit_msg(adap, &reply, false);
349			break;
350		}
351		return 0;
352	}
353	case CEC_MSG_VENDOR_COMMAND_WITH_ID: {
354		u32 vendor_id;
355		u8 size;
356		const u8 *vendor_cmd;
357
358		/*
359		 * If we receive <Vendor Command With ID> with our vendor ID
360		 * and with a payload of size 1, and the payload value is odd,
361		 * then we reply with the same message, but with the payload
362		 * byte incremented by 1.
363		 *
364		 * If the size is 1 and the payload value is even, then we
365		 * ignore the message.
366		 *
367		 * The reason we reply to odd instead of even payload values
368		 * is that it allows for testing of the corner case where the
369		 * reply value is 0 (0xff + 1 % 256).
370		 *
371		 * For other sizes we Feature Abort.
372		 *
373		 * This is added for the specific purpose of testing the
374		 * CEC_MSG_FL_REPLY_VENDOR_ID flag using vivid.
375		 */
376		cec_ops_vendor_command_with_id(msg, &vendor_id, &size, &vendor_cmd);
377		if (vendor_id != adap->log_addrs.vendor_id)
378			break;
379		if (size == 1) {
380			// Ignore even op values
381			if (!(vendor_cmd[0] & 1))
382				return 0;
383			reply.len = msg->len;
384			memcpy(reply.msg + 1, msg->msg + 1, msg->len - 1);
385			reply.msg[msg->len - 1]++;
386		} else {
387			cec_msg_feature_abort(&reply, cec_msg_opcode(msg),
388					      CEC_OP_ABORT_INVALID_OP);
389		}
390		cec_transmit_msg(adap, &reply, false);
391		return 0;
392	}
393	}
394	return -ENOMSG;
395}
396
397static const struct cec_adap_ops vivid_cec_adap_ops = {
398	.adap_enable = vivid_cec_adap_enable,
399	.adap_log_addr = vivid_cec_adap_log_addr,
400	.adap_transmit = vivid_cec_adap_transmit,
401	.received = vivid_received,
402};
403
404struct cec_adapter *vivid_cec_alloc_adap(struct vivid_dev *dev,
405					 unsigned int idx,
406					 bool is_source)
407{
408	u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN;
409	char name[32];
410
411	snprintf(name, sizeof(name), "vivid-%03d-vid-%s%d",
412		 dev->inst, is_source ? "out" : "cap", idx);
413	return cec_allocate_adapter(&vivid_cec_adap_ops, dev,
414				    name, caps, CEC_MAX_LOG_ADDRS);
415}
~/ Read-only file preview.