Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.9.4.
  1/*
  2 * Copyright 2012-15 Advanced Micro Devices, Inc.
  3 *
  4 * Permission is hereby granted, free of charge, to any person obtaining a
  5 * copy of this software and associated documentation files (the "Software"),
  6 * to deal in the Software without restriction, including without limitation
  7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8 * and/or sell copies of the Software, and to permit persons to whom the
  9 * Software is furnished to do so, subject to the following conditions:
 10 *
 11 * The above copyright notice and this permission notice shall be included in
 12 * all copies or substantial portions of the Software.
 13 *
 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 20 * OTHER DEALINGS IN THE SOFTWARE.
 21 *
 22 * Authors: AMD
 23 *
 24 */
 25
 26#include "dm_services.h"
 27
 28/*
 29 * Pre-requisites: headers required by header of this unit
 30 */
 31#include "include/i2caux_interface.h"
 32#include "engine.h"
 33
 34/*
 35 * Header of this unit
 36 */
 37
 38#include "aux_engine.h"
 39
 40/*
 41 * Post-requisites: headers required by this unit
 42 */
 43
 44#include "include/link_service_types.h"
 45
 46/*
 47 * This unit
 48 */
 49
 50enum {
 51	AUX_INVALID_REPLY_RETRY_COUNTER = 1,
 52	AUX_TIMED_OUT_RETRY_COUNTER = 2,
 53	AUX_DEFER_RETRY_COUNTER = 6
 54};
 55
 56#define FROM_ENGINE(ptr) \
 57	container_of((ptr), struct aux_engine, base)
 58#define DC_LOGGER \
 59	engine->base.ctx->logger
 60
 61enum i2caux_engine_type dal_aux_engine_get_engine_type(
 62	const struct engine *engine)
 63{
 64	return I2CAUX_ENGINE_TYPE_AUX;
 65}
 66
 67bool dal_aux_engine_acquire(
 68	struct engine *engine,
 69	struct ddc *ddc)
 70{
 71	struct aux_engine *aux_engine = FROM_ENGINE(engine);
 72
 73	enum gpio_result result;
 74	if (aux_engine->funcs->is_engine_available) {
 75		/*check whether SW could use the engine*/
 76		if (!aux_engine->funcs->is_engine_available(aux_engine)) {
 77			return false;
 78		}
 79	}
 80
 81	result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE,
 82		GPIO_DDC_CONFIG_TYPE_MODE_AUX);
 83
 84	if (result != GPIO_RESULT_OK)
 85		return false;
 86
 87	if (!aux_engine->funcs->acquire_engine(aux_engine)) {
 88		dal_ddc_close(ddc);
 89		return false;
 90	}
 91
 92	engine->ddc = ddc;
 93
 94	return true;
 95}
 96
 97struct read_command_context {
 98	uint8_t *buffer;
 99	uint32_t current_read_length;
100	uint32_t offset;
101	enum i2caux_transaction_status status;
102
103	struct aux_request_transaction_data request;
104	struct aux_reply_transaction_data reply;
105
106	uint8_t returned_byte;
107
108	uint32_t timed_out_retry_aux;
109	uint32_t invalid_reply_retry_aux;
110	uint32_t defer_retry_aux;
111	uint32_t defer_retry_i2c;
112	uint32_t invalid_reply_retry_aux_on_ack;
113
114	bool transaction_complete;
115	bool operation_succeeded;
116};
117
118static void process_read_reply(
119	struct aux_engine *engine,
120	struct read_command_context *ctx)
121{
122	engine->funcs->process_channel_reply(engine, &ctx->reply);
123
124	switch (ctx->reply.status) {
125	case AUX_TRANSACTION_REPLY_AUX_ACK:
126		ctx->defer_retry_aux = 0;
127		if (ctx->returned_byte > ctx->current_read_length) {
128			ctx->status =
129				I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
130			ctx->operation_succeeded = false;
131		} else {
132			ctx->current_read_length = ctx->returned_byte;
133			ctx->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED;
134			ctx->transaction_complete = true;
135			ctx->operation_succeeded = true;
136		}
137	break;
138	case AUX_TRANSACTION_REPLY_AUX_NACK:
139		ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK;
140		ctx->operation_succeeded = false;
141	break;
142	case AUX_TRANSACTION_REPLY_AUX_DEFER:
143		++ctx->defer_retry_aux;
144
145		if (ctx->defer_retry_aux > AUX_DEFER_RETRY_COUNTER) {
146			ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
147			ctx->operation_succeeded = false;
148		}
149	break;
150	case AUX_TRANSACTION_REPLY_I2C_DEFER:
151		ctx->defer_retry_aux = 0;
152
153		++ctx->defer_retry_i2c;
154
155		if (ctx->defer_retry_i2c > AUX_DEFER_RETRY_COUNTER) {
156			ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
157			ctx->operation_succeeded = false;
158		}
159	break;
160	default:
161		ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
162		ctx->operation_succeeded = false;
163	}
164}
165
166static void process_read_request(
167	struct aux_engine *engine,
168	struct read_command_context *ctx)
169{
170	enum aux_channel_operation_result operation_result;
171
172	engine->funcs->submit_channel_request(engine, &ctx->request);
173
174	operation_result = engine->funcs->get_channel_status(
175		engine, &ctx->returned_byte);
176
177	switch (operation_result) {
178	case AUX_CHANNEL_OPERATION_SUCCEEDED:
179		if (ctx->returned_byte > ctx->current_read_length) {
180			ctx->status =
181				I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
182			ctx->operation_succeeded = false;
183		} else {
184			ctx->timed_out_retry_aux = 0;
185			ctx->invalid_reply_retry_aux = 0;
186
187			ctx->reply.length = ctx->returned_byte;
188			ctx->reply.data = ctx->buffer;
189
190			process_read_reply(engine, ctx);
191		}
192	break;
193	case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY:
194		++ctx->invalid_reply_retry_aux;
195
196		if (ctx->invalid_reply_retry_aux >
197			AUX_INVALID_REPLY_RETRY_COUNTER) {
198			ctx->status =
199				I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
200			ctx->operation_succeeded = false;
201		} else
202			udelay(400);
203	break;
204	case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT:
205		++ctx->timed_out_retry_aux;
206
207		if (ctx->timed_out_retry_aux > AUX_TIMED_OUT_RETRY_COUNTER) {
208			ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
209			ctx->operation_succeeded = false;
210		} else {
211			/* DP 1.2a, table 2-58:
212			 * "S3: AUX Request CMD PENDING:
213			 * retry 3 times, with 400usec wait on each"
214			 * The HW timeout is set to 550usec,
215			 * so we should not wait here */
216		}
217	break;
218	default:
219		ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
220		ctx->operation_succeeded = false;
221	}
222}
223
224static bool read_command(
225	struct aux_engine *engine,
226	struct i2caux_transaction_request *request,
227	bool middle_of_transaction)
228{
229	struct read_command_context ctx;
230
231	ctx.buffer = request->payload.data;
232	ctx.current_read_length = request->payload.length;
233	ctx.offset = 0;
234	ctx.timed_out_retry_aux = 0;
235	ctx.invalid_reply_retry_aux = 0;
236	ctx.defer_retry_aux = 0;
237	ctx.defer_retry_i2c = 0;
238	ctx.invalid_reply_retry_aux_on_ack = 0;
239	ctx.transaction_complete = false;
240	ctx.operation_succeeded = true;
241
242	if (request->payload.address_space ==
243		I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) {
244		ctx.request.type = AUX_TRANSACTION_TYPE_DP;
245		ctx.request.action = I2CAUX_TRANSACTION_ACTION_DP_READ;
246		ctx.request.address = request->payload.address;
247	} else if (request->payload.address_space ==
248		I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) {
249		ctx.request.type = AUX_TRANSACTION_TYPE_I2C;
250		ctx.request.action = middle_of_transaction ?
251			I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT :
252			I2CAUX_TRANSACTION_ACTION_I2C_READ;
253		ctx.request.address = request->payload.address >> 1;
254	} else {
255		/* in DAL2, there was no return in such case */
256		BREAK_TO_DEBUGGER();
257		return false;
258	}
259
260	ctx.request.delay = 0;
261
262	do {
263		memset(ctx.buffer + ctx.offset, 0, ctx.current_read_length);
264
265		ctx.request.data = ctx.buffer + ctx.offset;
266		ctx.request.length = ctx.current_read_length;
267
268		process_read_request(engine, &ctx);
269
270		request->status = ctx.status;
271
272		if (ctx.operation_succeeded && !ctx.transaction_complete)
273			if (ctx.request.type == AUX_TRANSACTION_TYPE_I2C)
274				msleep(engine->delay);
275	} while (ctx.operation_succeeded && !ctx.transaction_complete);
276
277	if (request->payload.address_space ==
278		I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) {
279		DC_LOG_I2C_AUX("READ: addr:0x%x  value:0x%x Result:%d",
280				request->payload.address,
281				request->payload.data[0],
282				ctx.operation_succeeded);
283	}
284
285	request->payload.length = ctx.reply.length;
286	return ctx.operation_succeeded;
287}
288
289struct write_command_context {
290	bool mot;
291
292	uint8_t *buffer;
293	uint32_t current_write_length;
294	enum i2caux_transaction_status status;
295
296	struct aux_request_transaction_data request;
297	struct aux_reply_transaction_data reply;
298
299	uint8_t returned_byte;
300
301	uint32_t timed_out_retry_aux;
302	uint32_t invalid_reply_retry_aux;
303	uint32_t defer_retry_aux;
304	uint32_t defer_retry_i2c;
305	uint32_t max_defer_retry;
306	uint32_t ack_m_retry;
307
308	uint8_t reply_data[DEFAULT_AUX_MAX_DATA_SIZE];
309
310	bool transaction_complete;
311	bool operation_succeeded;
312};
313
314static void process_write_reply(
315	struct aux_engine *engine,
316	struct write_command_context *ctx)
317{
318	engine->funcs->process_channel_reply(engine, &ctx->reply);
319
320	switch (ctx->reply.status) {
321	case AUX_TRANSACTION_REPLY_AUX_ACK:
322		ctx->operation_succeeded = true;
323
324		if (ctx->returned_byte) {
325			ctx->request.action = ctx->mot ?
326			I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT :
327			I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST;
328
329			ctx->current_write_length = 0;
330
331			++ctx->ack_m_retry;
332
333			if (ctx->ack_m_retry > AUX_DEFER_RETRY_COUNTER) {
334				ctx->status =
335				I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
336				ctx->operation_succeeded = false;
337			} else
338				udelay(300);
339		} else {
340			ctx->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED;
341			ctx->defer_retry_aux = 0;
342			ctx->ack_m_retry = 0;
343			ctx->transaction_complete = true;
344		}
345	break;
346	case AUX_TRANSACTION_REPLY_AUX_NACK:
347		ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK;
348		ctx->operation_succeeded = false;
349	break;
350	case AUX_TRANSACTION_REPLY_AUX_DEFER:
351		++ctx->defer_retry_aux;
352
353		if (ctx->defer_retry_aux > ctx->max_defer_retry) {
354			ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
355			ctx->operation_succeeded = false;
356		}
357	break;
358	case AUX_TRANSACTION_REPLY_I2C_DEFER:
359		ctx->defer_retry_aux = 0;
360		ctx->current_write_length = 0;
361
362		ctx->request.action = ctx->mot ?
363			I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT :
364			I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST;
365
366		++ctx->defer_retry_i2c;
367
368		if (ctx->defer_retry_i2c > ctx->max_defer_retry) {
369			ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
370			ctx->operation_succeeded = false;
371		}
372	break;
373	default:
374		ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
375		ctx->operation_succeeded = false;
376	}
377}
378
379static void process_write_request(
380	struct aux_engine *engine,
381	struct write_command_context *ctx)
382{
383	enum aux_channel_operation_result operation_result;
384
385	engine->funcs->submit_channel_request(engine, &ctx->request);
386
387	operation_result = engine->funcs->get_channel_status(
388		engine, &ctx->returned_byte);
389
390	switch (operation_result) {
391	case AUX_CHANNEL_OPERATION_SUCCEEDED:
392		ctx->timed_out_retry_aux = 0;
393		ctx->invalid_reply_retry_aux = 0;
394
395		ctx->reply.length = ctx->returned_byte;
396		ctx->reply.data = ctx->reply_data;
397
398		process_write_reply(engine, ctx);
399	break;
400	case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY:
401		++ctx->invalid_reply_retry_aux;
402
403		if (ctx->invalid_reply_retry_aux >
404			AUX_INVALID_REPLY_RETRY_COUNTER) {
405			ctx->status =
406				I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
407			ctx->operation_succeeded = false;
408		} else
409			udelay(400);
410	break;
411	case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT:
412		++ctx->timed_out_retry_aux;
413
414		if (ctx->timed_out_retry_aux > AUX_TIMED_OUT_RETRY_COUNTER) {
415			ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
416			ctx->operation_succeeded = false;
417		} else {
418			/* DP 1.2a, table 2-58:
419			 * "S3: AUX Request CMD PENDING:
420			 * retry 3 times, with 400usec wait on each"
421			 * The HW timeout is set to 550usec,
422			 * so we should not wait here */
423		}
424	break;
425	default:
426		ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
427		ctx->operation_succeeded = false;
428	}
429}
430
431static bool write_command(
432	struct aux_engine *engine,
433	struct i2caux_transaction_request *request,
434	bool middle_of_transaction)
435{
436	struct write_command_context ctx;
437
438	ctx.mot = middle_of_transaction;
439	ctx.buffer = request->payload.data;
440	ctx.current_write_length = request->payload.length;
441	ctx.timed_out_retry_aux = 0;
442	ctx.invalid_reply_retry_aux = 0;
443	ctx.defer_retry_aux = 0;
444	ctx.defer_retry_i2c = 0;
445	ctx.ack_m_retry = 0;
446	ctx.transaction_complete = false;
447	ctx.operation_succeeded = true;
448
449	if (request->payload.address_space ==
450		I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) {
451		ctx.request.type = AUX_TRANSACTION_TYPE_DP;
452		ctx.request.action = I2CAUX_TRANSACTION_ACTION_DP_WRITE;
453		ctx.request.address = request->payload.address;
454	} else if (request->payload.address_space ==
455		I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) {
456		ctx.request.type = AUX_TRANSACTION_TYPE_I2C;
457		ctx.request.action = middle_of_transaction ?
458			I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT :
459			I2CAUX_TRANSACTION_ACTION_I2C_WRITE;
460		ctx.request.address = request->payload.address >> 1;
461	} else {
462		/* in DAL2, there was no return in such case */
463		BREAK_TO_DEBUGGER();
464		return false;
465	}
466
467	ctx.request.delay = 0;
468
469	ctx.max_defer_retry =
470		(engine->max_defer_write_retry > AUX_DEFER_RETRY_COUNTER) ?
471			engine->max_defer_write_retry : AUX_DEFER_RETRY_COUNTER;
472
473	do {
474		ctx.request.data = ctx.buffer;
475		ctx.request.length = ctx.current_write_length;
476
477		process_write_request(engine, &ctx);
478
479		request->status = ctx.status;
480
481		if (ctx.operation_succeeded && !ctx.transaction_complete)
482			if (ctx.request.type == AUX_TRANSACTION_TYPE_I2C)
483				msleep(engine->delay);
484	} while (ctx.operation_succeeded && !ctx.transaction_complete);
485
486	if (request->payload.address_space ==
487		I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) {
488		DC_LOG_I2C_AUX("WRITE: addr:0x%x  value:0x%x Result:%d",
489				request->payload.address,
490				request->payload.data[0],
491				ctx.operation_succeeded);
492	}
493
494	return ctx.operation_succeeded;
495}
496
497static bool end_of_transaction_command(
498	struct aux_engine *engine,
499	struct i2caux_transaction_request *request)
500{
501	struct i2caux_transaction_request dummy_request;
502	uint8_t dummy_data;
503
504	/* [tcheng] We only need to send the stop (read with MOT = 0)
505	 * for I2C-over-Aux, not native AUX */
506
507	if (request->payload.address_space !=
508		I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C)
509		return false;
510
511	dummy_request.operation = request->operation;
512	dummy_request.payload.address_space = request->payload.address_space;
513	dummy_request.payload.address = request->payload.address;
514
515	/*
516	 * Add a dummy byte due to some receiver quirk
517	 * where one byte is sent along with MOT = 0.
518	 * Ideally this should be 0.
519	 */
520
521	dummy_request.payload.length = 0;
522	dummy_request.payload.data = &dummy_data;
523
524	if (request->operation == I2CAUX_TRANSACTION_READ)
525		return read_command(engine, &dummy_request, false);
526	else
527		return write_command(engine, &dummy_request, false);
528
529	/* according Syed, it does not need now DoDummyMOT */
530}
531
532bool dal_aux_engine_submit_request(
533	struct engine *engine,
534	struct i2caux_transaction_request *request,
535	bool middle_of_transaction)
536{
537	struct aux_engine *aux_engine = FROM_ENGINE(engine);
538
539	bool result;
540	bool mot_used = true;
541
542	switch (request->operation) {
543	case I2CAUX_TRANSACTION_READ:
544		result = read_command(aux_engine, request, mot_used);
545	break;
546	case I2CAUX_TRANSACTION_WRITE:
547		result = write_command(aux_engine, request, mot_used);
548	break;
549	default:
550		result = false;
551	}
552
553	/* [tcheng]
554	 * need to send stop for the last transaction to free up the AUX
555	 * if the above command fails, this would be the last transaction */
556
557	if (!middle_of_transaction || !result)
558		end_of_transaction_command(aux_engine, request);
559
560	/* mask AUX interrupt */
561
562	return result;
563}
564
565void dal_aux_engine_construct(
566	struct aux_engine *engine,
567	struct dc_context *ctx)
568{
569	dal_i2caux_construct_engine(&engine->base, ctx);
570	engine->delay = 0;
571	engine->max_defer_write_retry = 0;
572}
573
574void dal_aux_engine_destruct(
575	struct aux_engine *engine)
576{
577	dal_i2caux_destruct_engine(&engine->base);
578}