Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * Driver for Analog Devices ADV748X video decoder and HDMI receiver
 *
 * Copyright (C) 2017 Renesas Electronics Corp.
 *
 * Authors:
 *	Koji Matsuoka <koji.matsuoka.xm@renesas.com>
 *	Niklas Söderlund <niklas.soderlund@ragnatech.se>
 *	Kieran Bingham <kieran.bingham@ideasonboard.com>
 *
 * The ADV748x range of receivers have the following configurations:
 *
 *                  Analog   HDMI  MHL  4-Lane  1-Lane
 *                    In      In         CSI     CSI
 *       ADV7480               X    X     X
 *       ADV7481      X        X    X     X       X
 *       ADV7482      X        X          X       X
 */

#include <linux/i2c.h>

#ifndef _ADV748X_H_
#define _ADV748X_H_

enum adv748x_page {
	ADV748X_PAGE_IO,
	ADV748X_PAGE_DPLL,
	ADV748X_PAGE_CP,
	ADV748X_PAGE_HDMI,
	ADV748X_PAGE_EDID,
	ADV748X_PAGE_REPEATER,
	ADV748X_PAGE_INFOFRAME,
	ADV748X_PAGE_CBUS,
	ADV748X_PAGE_CEC,
	ADV748X_PAGE_SDP,
	ADV748X_PAGE_TXB,
	ADV748X_PAGE_TXA,
	ADV748X_PAGE_MAX,

	/* Fake pages for register sequences */
	ADV748X_PAGE_EOR,		/* End Mark */
};

/*
 * Device tree port number definitions
 *
 * The ADV748X ports define the mapping between subdevices
 * and the device tree specification
 */
enum adv748x_ports {
	ADV748X_PORT_AIN0 = 0,
	ADV748X_PORT_AIN1 = 1,
	ADV748X_PORT_AIN2 = 2,
	ADV748X_PORT_AIN3 = 3,
	ADV748X_PORT_AIN4 = 4,
	ADV748X_PORT_AIN5 = 5,
	ADV748X_PORT_AIN6 = 6,
	ADV748X_PORT_AIN7 = 7,
	ADV748X_PORT_HDMI = 8,
	ADV748X_PORT_TTL = 9,
	ADV748X_PORT_TXA = 10,
	ADV748X_PORT_TXB = 11,
	ADV748X_PORT_MAX = 12,
};

enum adv748x_csi2_pads {
	ADV748X_CSI2_SINK,
	ADV748X_CSI2_SOURCE,
	ADV748X_CSI2_NR_PADS,
};

/* CSI2 transmitters can have 2 internal connections, HDMI/AFE */
#define ADV748X_CSI2_MAX_SUBDEVS 2

struct adv748x_csi2 {
	struct adv748x_state *state;
	struct v4l2_mbus_framefmt format;
	unsigned int page;
	unsigned int port;
	unsigned int num_lanes;
	unsigned int active_lanes;

	struct media_pad pads[ADV748X_CSI2_NR_PADS];
	struct v4l2_ctrl_handler ctrl_hdl;
	struct v4l2_ctrl *pixel_rate;
	struct v4l2_subdev *src;
	struct v4l2_subdev sd;
};

#define notifier_to_csi2(n) container_of(n, struct adv748x_csi2, notifier)
#define adv748x_sd_to_csi2(sd) container_of(sd, struct adv748x_csi2, sd)

#define is_tx_enabled(_tx) ((_tx)->state->endpoints[(_tx)->port] != NULL)
#define is_txa(_tx) ((_tx) == &(_tx)->state->txa)
#define is_txb(_tx) ((_tx) == &(_tx)->state->txb)
#define is_tx(_tx) (is_txa(_tx) || is_txb(_tx))

#define is_afe_enabled(_state)					\
	((_state)->endpoints[ADV748X_PORT_AIN0] != NULL ||	\
	 (_state)->endpoints[ADV748X_PORT_AIN1] != NULL ||	\
	 (_state)->endpoints[ADV748X_PORT_AIN2] != NULL ||	\
	 (_state)->endpoints[ADV748X_PORT_AIN3] != NULL ||	\
	 (_state)->endpoints[ADV748X_PORT_AIN4] != NULL ||	\
	 (_state)->endpoints[ADV748X_PORT_AIN5] != NULL ||	\
	 (_state)->endpoints[ADV748X_PORT_AIN6] != NULL ||	\
	 (_state)->endpoints[ADV748X_PORT_AIN7] != NULL)
#define is_hdmi_enabled(_state) ((_state)->endpoints[ADV748X_PORT_HDMI] != NULL)

enum adv748x_hdmi_pads {
	ADV748X_HDMI_SINK,
	ADV748X_HDMI_SOURCE,
	ADV748X_HDMI_NR_PADS,
};

struct adv748x_hdmi {
	struct media_pad pads[ADV748X_HDMI_NR_PADS];
	struct v4l2_ctrl_handler ctrl_hdl;
	struct v4l2_subdev sd;
	struct v4l2_mbus_framefmt format;

	struct v4l2_dv_timings timings;
	struct v4l2_fract aspect_ratio;

	struct adv748x_csi2 *tx;

	struct {
		u8 edid[512];
		u32 present;
		unsigned int blocks;
	} edid;
};

#define adv748x_ctrl_to_hdmi(ctrl) \
	container_of(ctrl->handler, struct adv748x_hdmi, ctrl_hdl)
#define adv748x_sd_to_hdmi(sd) container_of(sd, struct adv748x_hdmi, sd)

enum adv748x_afe_pads {
	ADV748X_AFE_SINK_AIN0,
	ADV748X_AFE_SINK_AIN1,
	ADV748X_AFE_SINK_AIN2,
	ADV748X_AFE_SINK_AIN3,
	ADV748X_AFE_SINK_AIN4,
	ADV748X_AFE_SINK_AIN5,
	ADV748X_AFE_SINK_AIN6,
	ADV748X_AFE_SINK_AIN7,
	ADV748X_AFE_SOURCE,
	ADV748X_AFE_NR_PADS,
};

struct adv748x_afe {
	struct media_pad pads[ADV748X_AFE_NR_PADS];
	struct v4l2_ctrl_handler ctrl_hdl;
	struct v4l2_subdev sd;
	struct v4l2_mbus_framefmt format;

	struct adv748x_csi2 *tx;

	bool streaming;
	v4l2_std_id curr_norm;
	unsigned int input;
};

#define adv748x_ctrl_to_afe(ctrl) \
	container_of(ctrl->handler, struct adv748x_afe, ctrl_hdl)
#define adv748x_sd_to_afe(sd) container_of(sd, struct adv748x_afe, sd)

/**
 * struct adv748x_state - State of ADV748X
 * @dev:		(OF) device
 * @client:		I2C client
 * @mutex:		protect global state
 *
 * @endpoints:		parsed device node endpoints for each port
 *
 * @i2c_addresses:	I2C Page addresses
 * @i2c_clients:	I2C clients for the page accesses
 * @regmap:		regmap configuration pages.
 *
 * @hdmi:		state of HDMI receiver context
 * @afe:		state of AFE receiver context
 * @txa:		state of TXA transmitter context
 * @txb:		state of TXB transmitter context
 */
struct adv748x_state {
	struct device *dev;
	struct i2c_client *client;
	struct mutex mutex;

	struct device_node *endpoints[ADV748X_PORT_MAX];

	struct i2c_client *i2c_clients[ADV748X_PAGE_MAX];
	struct regmap *regmap[ADV748X_PAGE_MAX];

	struct adv748x_hdmi hdmi;
	struct adv748x_afe afe;
	struct adv748x_csi2 txa;
	struct adv748x_csi2 txb;
};

#define adv748x_hdmi_to_state(h) container_of(h, struct adv748x_state, hdmi)
#define adv748x_afe_to_state(a) container_of(a, struct adv748x_state, afe)

#define adv_err(a, fmt, arg...)	dev_err(a->dev, fmt, ##arg)
#define adv_info(a, fmt, arg...) dev_info(a->dev, fmt, ##arg)
#define adv_dbg(a, fmt, arg...)	dev_dbg(a->dev, fmt, ##arg)

/* Register Mappings */

/* IO Map */
#define ADV748X_IO_PD			0x00	/* power down controls */
#define ADV748X_IO_PD_RX_EN		BIT(6)

#define ADV748X_IO_REG_01		0x01	/* pwrdn{2}b, prog_xtal_freq */
#define ADV748X_IO_REG_01_PWRDN_MASK	(BIT(7) | BIT(6))
#define ADV748X_IO_REG_01_PWRDN2B	BIT(7)	/* CEC Wakeup Support */
#define ADV748X_IO_REG_01_PWRDNB	BIT(6)	/* CEC Wakeup Support */

#define ADV748X_IO_REG_04		0x04
#define ADV748X_IO_REG_04_FORCE_FR	BIT(0)	/* Force CP free-run */

#define ADV748X_IO_DATAPATH		0x03	/* datapath cntrl */
#define ADV748X_IO_DATAPATH_VFREQ_M	0x70
#define ADV748X_IO_DATAPATH_VFREQ_SHIFT	4

#define ADV748X_IO_VID_STD		0x05

#define ADV748X_IO_10			0x10	/* io_reg_10 */
#define ADV748X_IO_10_CSI4_EN		BIT(7)
#define ADV748X_IO_10_CSI1_EN		BIT(6)
#define ADV748X_IO_10_PIX_OUT_EN	BIT(5)
#define ADV748X_IO_10_CSI4_IN_SEL_AFE	BIT(3)

#define ADV748X_IO_CHIP_REV_ID_1	0xdf
#define ADV748X_IO_CHIP_REV_ID_2	0xe0

#define ADV748X_IO_REG_F2		0xf2
#define ADV748X_IO_REG_F2_READ_AUTO_INC	BIT(0)

/* For PAGE slave address offsets */
#define ADV748X_IO_SLAVE_ADDR_BASE	0xf2

/*
 * The ADV748x_Recommended_Settings_PrA_2014-08-20.pdf details both 0x80 and
 * 0xff as examples for performing a software reset.
 */
#define ADV748X_IO_REG_FF		0xff
#define ADV748X_IO_REG_FF_MAIN_RESET	0xff

/* HDMI RX Map */
#define ADV748X_HDMI_LW1		0x07	/* line width_1 */
#define ADV748X_HDMI_LW1_VERT_FILTER	BIT(7)
#define ADV748X_HDMI_LW1_DE_REGEN	BIT(5)
#define ADV748X_HDMI_LW1_WIDTH_MASK	0x1fff

#define ADV748X_HDMI_F0H1		0x09	/* field0 height_1 */
#define ADV748X_HDMI_F0H1_HEIGHT_MASK	0x1fff

#define ADV748X_HDMI_F1H1		0x0b	/* field1 height_1 */
#define ADV748X_HDMI_F1H1_INTERLACED	BIT(5)

#define ADV748X_HDMI_HFRONT_PORCH	0x20	/* hsync_front_porch_1 */
#define ADV748X_HDMI_HFRONT_PORCH_MASK	0x1fff

#define ADV748X_HDMI_HSYNC_WIDTH	0x22	/* hsync_pulse_width_1 */
#define ADV748X_HDMI_HSYNC_WIDTH_MASK	0x1fff

#define ADV748X_HDMI_HBACK_PORCH	0x24	/* hsync_back_porch_1 */
#define ADV748X_HDMI_HBACK_PORCH_MASK	0x1fff

#define ADV748X_HDMI_VFRONT_PORCH	0x2a	/* field0_vs_front_porch_1 */
#define ADV748X_HDMI_VFRONT_PORCH_MASK	0x3fff

#define ADV748X_HDMI_VSYNC_WIDTH	0x2e	/* field0_vs_pulse_width_1 */
#define ADV748X_HDMI_VSYNC_WIDTH_MASK	0x3fff

#define ADV748X_HDMI_VBACK_PORCH	0x32	/* field0_vs_back_porch_1 */
#define ADV748X_HDMI_VBACK_PORCH_MASK	0x3fff

#define ADV748X_HDMI_TMDS_1		0x51	/* hdmi_reg_51 */
#define ADV748X_HDMI_TMDS_2		0x52	/* hdmi_reg_52 */

/* HDMI RX Repeater Map */
#define ADV748X_REPEATER_EDID_SZ	0x70	/* primary_edid_size */
#define ADV748X_REPEATER_EDID_SZ_SHIFT	4

#define ADV748X_REPEATER_EDID_CTL	0x74	/* hdcp edid controls */
#define ADV748X_REPEATER_EDID_CTL_EN	BIT(0)	/* man_edid_a_enable */

/* SDP Main Map */
#define ADV748X_SDP_INSEL		0x00	/* user_map_rw_reg_00 */

#define ADV748X_SDP_VID_SEL		0x02	/* user_map_rw_reg_02 */
#define ADV748X_SDP_VID_SEL_MASK	0xf0
#define ADV748X_SDP_VID_SEL_SHIFT	4

/* Contrast - Unsigned*/
#define ADV748X_SDP_CON			0x08	/* user_map_rw_reg_08 */
#define ADV748X_SDP_CON_MIN		0
#define ADV748X_SDP_CON_DEF		128
#define ADV748X_SDP_CON_MAX		255

/* Brightness - Signed */
#define ADV748X_SDP_BRI			0x0a	/* user_map_rw_reg_0a */
#define ADV748X_SDP_BRI_MIN		-128
#define ADV748X_SDP_BRI_DEF		0
#define ADV748X_SDP_BRI_MAX		127

/* Hue - Signed, inverted*/
#define ADV748X_SDP_HUE			0x0b	/* user_map_rw_reg_0b */
#define ADV748X_SDP_HUE_MIN		-127
#define ADV748X_SDP_HUE_DEF		0
#define ADV748X_SDP_HUE_MAX		128

/* Test Patterns / Default Values */
#define ADV748X_SDP_DEF			0x0c	/* user_map_rw_reg_0c */
#define ADV748X_SDP_DEF_VAL_EN		BIT(0)	/* Force free run mode */
#define ADV748X_SDP_DEF_VAL_AUTO_EN	BIT(1)	/* Free run when no signal */

#define ADV748X_SDP_MAP_SEL		0x0e	/* user_map_rw_reg_0e */
#define ADV748X_SDP_MAP_SEL_RO_MAIN	1

/* Free run pattern select */
#define ADV748X_SDP_FRP			0x14
#define ADV748X_SDP_FRP_MASK		GENMASK(3, 1)

/* Saturation */
#define ADV748X_SDP_SD_SAT_U		0xe3	/* user_map_rw_reg_e3 */
#define ADV748X_SDP_SD_SAT_V		0xe4	/* user_map_rw_reg_e4 */
#define ADV748X_SDP_SAT_MIN		0
#define ADV748X_SDP_SAT_DEF		128
#define ADV748X_SDP_SAT_MAX		255

/* SDP RO Main Map */
#define ADV748X_SDP_RO_10		0x10
#define ADV748X_SDP_RO_10_IN_LOCK	BIT(0)

/* CP Map */
#define ADV748X_CP_PAT_GEN		0x37	/* int_pat_gen_1 */
#define ADV748X_CP_PAT_GEN_EN		BIT(7)

/* Contrast Control - Unsigned */
#define ADV748X_CP_CON			0x3a	/* contrast_cntrl */
#define ADV748X_CP_CON_MIN		0	/* Minimum contrast */
#define ADV748X_CP_CON_DEF		128	/* Default */
#define ADV748X_CP_CON_MAX		255	/* Maximum contrast */

/* Saturation Control - Unsigned */
#define ADV748X_CP_SAT			0x3b	/* saturation_cntrl */
#define ADV748X_CP_SAT_MIN		0	/* Minimum saturation */
#define ADV748X_CP_SAT_DEF		128	/* Default */
#define ADV748X_CP_SAT_MAX		255	/* Maximum saturation */

/* Brightness Control - Signed */
#define ADV748X_CP_BRI			0x3c	/* brightness_cntrl */
#define ADV748X_CP_BRI_MIN		-128	/* Luma is -512d */
#define ADV748X_CP_BRI_DEF		0	/* Luma is 0 */
#define ADV748X_CP_BRI_MAX		127	/* Luma is 508d */

/* Hue Control */
#define ADV748X_CP_HUE			0x3d	/* hue_cntrl */
#define ADV748X_CP_HUE_MIN		0	/* -90 degree */
#define ADV748X_CP_HUE_DEF		0	/* -90 degree */
#define ADV748X_CP_HUE_MAX		255	/* +90 degree */

#define ADV748X_CP_VID_ADJ		0x3e	/* vid_adj_0 */
#define ADV748X_CP_VID_ADJ_ENABLE	BIT(7)	/* Enable colour controls */

#define ADV748X_CP_DE_POS_HIGH		0x8b	/* de_pos_adj_6 */
#define ADV748X_CP_DE_POS_HIGH_SET	BIT(6)
#define ADV748X_CP_DE_POS_END_LOW	0x8c	/* de_pos_adj_7 */
#define ADV748X_CP_DE_POS_START_LOW	0x8d	/* de_pos_adj_8 */

#define ADV748X_CP_VID_ADJ_2			0x91
#define ADV748X_CP_VID_ADJ_2_INTERLACED		BIT(6)
#define ADV748X_CP_VID_ADJ_2_INTERLACED_3D	BIT(4)

#define ADV748X_CP_CLMP_POS		0xc9	/* clmp_pos_cntrl_4 */
#define ADV748X_CP_CLMP_POS_DIS_AUTO	BIT(0)	/* dis_auto_param_buff */

/* CSI : TXA/TXB Maps */
#define ADV748X_CSI_VC_REF		0x0d	/* csi_tx_top_reg_0d */
#define ADV748X_CSI_VC_REF_SHIFT	6

#define ADV748X_CSI_FS_AS_LS		0x1e	/* csi_tx_top_reg_1e */
#define ADV748X_CSI_FS_AS_LS_UNKNOWN	BIT(6)	/* Undocumented bit */

/* Register handling */

int adv748x_read(struct adv748x_state *state, u8 addr, u8 reg);
int adv748x_write(struct adv748x_state *state, u8 page, u8 reg, u8 value);
int adv748x_write_block(struct adv748x_state *state, int client_page,
			unsigned int init_reg, const void *val,
			size_t val_len);

#define io_read(s, r) adv748x_read(s, ADV748X_PAGE_IO, r)
#define io_write(s, r, v) adv748x_write(s, ADV748X_PAGE_IO, r, v)
#define io_clrset(s, r, m, v) io_write(s, r, (io_read(s, r) & ~(m)) | (v))

#define hdmi_read(s, r) adv748x_read(s, ADV748X_PAGE_HDMI, r)
#define hdmi_read16(s, r, m) (((hdmi_read(s, r) << 8) | hdmi_read(s, (r)+1)) & (m))
#define hdmi_write(s, r, v) adv748x_write(s, ADV748X_PAGE_HDMI, r, v)

#define repeater_read(s, r) adv748x_read(s, ADV748X_PAGE_REPEATER, r)
#define repeater_write(s, r, v) adv748x_write(s, ADV748X_PAGE_REPEATER, r, v)

#define sdp_read(s, r) adv748x_read(s, ADV748X_PAGE_SDP, r)
#define sdp_write(s, r, v) adv748x_write(s, ADV748X_PAGE_SDP, r, v)
#define sdp_clrset(s, r, m, v) sdp_write(s, r, (sdp_read(s, r) & ~(m)) | (v))

#define cp_read(s, r) adv748x_read(s, ADV748X_PAGE_CP, r)
#define cp_write(s, r, v) adv748x_write(s, ADV748X_PAGE_CP, r, v)
#define cp_clrset(s, r, m, v) cp_write(s, r, (cp_read(s, r) & ~(m)) | (v))

#define tx_read(t, r) adv748x_read(t->state, t->page, r)
#define tx_write(t, r, v) adv748x_write(t->state, t->page, r, v)

static inline struct v4l2_subdev *adv748x_get_remote_sd(struct media_pad *pad)
{
	pad = media_pad_remote_pad_first(pad);
	if (!pad)
		return NULL;

	return media_entity_to_v4l2_subdev(pad->entity);
}

void adv748x_subdev_init(struct v4l2_subdev *sd, struct adv748x_state *state,
			 const struct v4l2_subdev_ops *ops, u32 function,
			 const char *ident);

int adv748x_tx_power(struct adv748x_csi2 *tx, bool on);

int adv748x_afe_init(struct adv748x_afe *afe);
void adv748x_afe_cleanup(struct adv748x_afe *afe);
int adv748x_afe_s_input(struct adv748x_afe *afe, unsigned int input);

int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx);
void adv748x_csi2_cleanup(struct adv748x_csi2 *tx);
int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, unsigned int vc);
int adv748x_csi2_set_pixelrate(struct v4l2_subdev *sd, s64 rate);

int adv748x_hdmi_init(struct adv748x_hdmi *hdmi);
void adv748x_hdmi_cleanup(struct adv748x_hdmi *hdmi);

#endif /* _ADV748X_H_ */