File Index
Files and directories
Sign in
FILE

sil164_drv.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/nouveau/dispnv04/i2c/sil164_drv.c C
  1/*
  2 * Copyright (C) 2010 Francisco Jerez.
  3 * All Rights Reserved.
  4 *
  5 * Permission is hereby granted, free of charge, to any person obtaining
  6 * a copy of this software and associated documentation files (the
  7 * "Software"), to deal in the Software without restriction, including
  8 * without limitation the rights to use, copy, modify, merge, publish,
  9 * distribute, sublicense, and/or sell copies of the Software, and to
 10 * permit persons to whom the Software is furnished to do so, subject to
 11 * the following conditions:
 12 *
 13 * The above copyright notice and this permission notice (including the
 14 * next paragraph) shall be included in all copies or substantial
 15 * portions of the Software.
 16 *
 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 20 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
 21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 24 *
 25 */
 26
 27#include <linux/module.h>
 28
 29#include <drm/drm_drv.h>
 30#include <drm/drm_print.h>
 31#include <drm/drm_probe_helper.h>
 32
 33#include <dispnv04/i2c/encoder_i2c.h>
 34#include <dispnv04/i2c/sil164.h>
 35
 36struct sil164_priv {
 37	struct sil164_encoder_params config;
 38	struct i2c_client *duallink_slave;
 39
 40	uint8_t saved_state[0x10];
 41	uint8_t saved_slave_state[0x10];
 42};
 43
 44#define to_sil164_priv(x) \
 45	((struct sil164_priv *)to_encoder_i2c(x)->encoder_i2c_priv)
 46
 47#define sil164_dbg(client, format, ...) do {				\
 48		if (drm_debug_enabled(DRM_UT_KMS))			\
 49			dev_printk(KERN_DEBUG, &client->dev,		\
 50				   "%s: " format, __func__, ## __VA_ARGS__); \
 51	} while (0)
 52#define sil164_info(client, format, ...)		\
 53	dev_info(&client->dev, format, __VA_ARGS__)
 54#define sil164_err(client, format, ...)			\
 55	dev_err(&client->dev, format, __VA_ARGS__)
 56
 57#define SIL164_I2C_ADDR_MASTER			0x38
 58#define SIL164_I2C_ADDR_SLAVE			0x39
 59
 60/* HW register definitions */
 61
 62#define SIL164_VENDOR_LO			0x0
 63#define SIL164_VENDOR_HI			0x1
 64#define SIL164_DEVICE_LO			0x2
 65#define SIL164_DEVICE_HI			0x3
 66#define SIL164_REVISION				0x4
 67#define SIL164_FREQ_MIN				0x6
 68#define SIL164_FREQ_MAX				0x7
 69#define SIL164_CONTROL0				0x8
 70#  define SIL164_CONTROL0_POWER_ON		0x01
 71#  define SIL164_CONTROL0_EDGE_RISING		0x02
 72#  define SIL164_CONTROL0_INPUT_24BIT		0x04
 73#  define SIL164_CONTROL0_DUAL_EDGE		0x08
 74#  define SIL164_CONTROL0_HSYNC_ON		0x10
 75#  define SIL164_CONTROL0_VSYNC_ON		0x20
 76#define SIL164_DETECT				0x9
 77#  define SIL164_DETECT_INTR_STAT		0x01
 78#  define SIL164_DETECT_HOTPLUG_STAT		0x02
 79#  define SIL164_DETECT_RECEIVER_STAT		0x04
 80#  define SIL164_DETECT_INTR_MODE_RECEIVER	0x00
 81#  define SIL164_DETECT_INTR_MODE_HOTPLUG	0x08
 82#  define SIL164_DETECT_OUT_MODE_HIGH		0x00
 83#  define SIL164_DETECT_OUT_MODE_INTR		0x10
 84#  define SIL164_DETECT_OUT_MODE_RECEIVER	0x20
 85#  define SIL164_DETECT_OUT_MODE_HOTPLUG	0x30
 86#  define SIL164_DETECT_VSWING_STAT		0x80
 87#define SIL164_CONTROL1				0xa
 88#  define SIL164_CONTROL1_DESKEW_ENABLE		0x10
 89#  define SIL164_CONTROL1_DESKEW_INCR_SHIFT	5
 90#define SIL164_GPIO				0xb
 91#define SIL164_CONTROL2				0xc
 92#  define SIL164_CONTROL2_FILTER_ENABLE		0x01
 93#  define SIL164_CONTROL2_FILTER_SETTING_SHIFT	1
 94#  define SIL164_CONTROL2_DUALLINK_MASTER	0x40
 95#  define SIL164_CONTROL2_SYNC_CONT		0x80
 96#define SIL164_DUALLINK				0xd
 97#  define SIL164_DUALLINK_ENABLE		0x10
 98#  define SIL164_DUALLINK_SKEW_SHIFT		5
 99#define SIL164_PLLZONE				0xe
100#  define SIL164_PLLZONE_STAT			0x08
101#  define SIL164_PLLZONE_FORCE_ON		0x10
102#  define SIL164_PLLZONE_FORCE_HIGH		0x20
103
104/* HW access functions */
105
106static void
107sil164_write(struct i2c_client *client, uint8_t addr, uint8_t val)
108{
109	uint8_t buf[] = {addr, val};
110	int ret;
111
112	ret = i2c_master_send(client, buf, ARRAY_SIZE(buf));
113	if (ret < 0)
114		sil164_err(client, "Error %d writing to subaddress 0x%x\n",
115			   ret, addr);
116}
117
118static uint8_t
119sil164_read(struct i2c_client *client, uint8_t addr)
120{
121	uint8_t val;
122	int ret;
123
124	ret = i2c_master_send(client, &addr, sizeof(addr));
125	if (ret < 0)
126		goto fail;
127
128	ret = i2c_master_recv(client, &val, sizeof(val));
129	if (ret < 0)
130		goto fail;
131
132	return val;
133
134fail:
135	sil164_err(client, "Error %d reading from subaddress 0x%x\n",
136		   ret, addr);
137	return 0;
138}
139
140static void
141sil164_save_state(struct i2c_client *client, uint8_t *state)
142{
143	int i;
144
145	for (i = 0x8; i <= 0xe; i++)
146		state[i] = sil164_read(client, i);
147}
148
149static void
150sil164_restore_state(struct i2c_client *client, uint8_t *state)
151{
152	int i;
153
154	for (i = 0x8; i <= 0xe; i++)
155		sil164_write(client, i, state[i]);
156}
157
158static void
159sil164_set_power_state(struct i2c_client *client, bool on)
160{
161	uint8_t control0 = sil164_read(client, SIL164_CONTROL0);
162
163	if (on)
164		control0 |= SIL164_CONTROL0_POWER_ON;
165	else
166		control0 &= ~SIL164_CONTROL0_POWER_ON;
167
168	sil164_write(client, SIL164_CONTROL0, control0);
169}
170
171static void
172sil164_init_state(struct i2c_client *client,
173		  struct sil164_encoder_params *config,
174		  bool duallink)
175{
176	sil164_write(client, SIL164_CONTROL0,
177		     SIL164_CONTROL0_HSYNC_ON |
178		     SIL164_CONTROL0_VSYNC_ON |
179		     (config->input_edge ? SIL164_CONTROL0_EDGE_RISING : 0) |
180		     (config->input_width ? SIL164_CONTROL0_INPUT_24BIT : 0) |
181		     (config->input_dual ? SIL164_CONTROL0_DUAL_EDGE : 0));
182
183	sil164_write(client, SIL164_DETECT,
184		     SIL164_DETECT_INTR_STAT |
185		     SIL164_DETECT_OUT_MODE_RECEIVER);
186
187	sil164_write(client, SIL164_CONTROL1,
188		     (config->input_skew ? SIL164_CONTROL1_DESKEW_ENABLE : 0) |
189		     (((config->input_skew + 4) & 0x7)
190		      << SIL164_CONTROL1_DESKEW_INCR_SHIFT));
191
192	sil164_write(client, SIL164_CONTROL2,
193		     SIL164_CONTROL2_SYNC_CONT |
194		     (config->pll_filter ? 0 : SIL164_CONTROL2_FILTER_ENABLE) |
195		     (4 << SIL164_CONTROL2_FILTER_SETTING_SHIFT));
196
197	sil164_write(client, SIL164_PLLZONE, 0);
198
199	if (duallink)
200		sil164_write(client, SIL164_DUALLINK,
201			     SIL164_DUALLINK_ENABLE |
202			     (((config->duallink_skew + 4) & 0x7)
203			      << SIL164_DUALLINK_SKEW_SHIFT));
204	else
205		sil164_write(client, SIL164_DUALLINK, 0);
206}
207
208/* DRM encoder functions */
209
210static void
211sil164_encoder_set_config(struct drm_encoder *encoder, void *params)
212{
213	struct sil164_priv *priv = to_sil164_priv(encoder);
214
215	priv->config = *(struct sil164_encoder_params *)params;
216}
217
218static void
219sil164_encoder_dpms(struct drm_encoder *encoder, int mode)
220{
221	struct sil164_priv *priv = to_sil164_priv(encoder);
222	bool on = (mode == DRM_MODE_DPMS_ON);
223	bool duallink = (on && encoder->crtc->mode.clock > 165000);
224
225	sil164_set_power_state(nouveau_i2c_encoder_get_client(encoder), on);
226
227	if (priv->duallink_slave)
228		sil164_set_power_state(priv->duallink_slave, duallink);
229}
230
231static void
232sil164_encoder_save(struct drm_encoder *encoder)
233{
234	struct sil164_priv *priv = to_sil164_priv(encoder);
235
236	sil164_save_state(nouveau_i2c_encoder_get_client(encoder),
237			  priv->saved_state);
238
239	if (priv->duallink_slave)
240		sil164_save_state(priv->duallink_slave,
241				  priv->saved_slave_state);
242}
243
244static void
245sil164_encoder_restore(struct drm_encoder *encoder)
246{
247	struct sil164_priv *priv = to_sil164_priv(encoder);
248
249	sil164_restore_state(nouveau_i2c_encoder_get_client(encoder),
250			     priv->saved_state);
251
252	if (priv->duallink_slave)
253		sil164_restore_state(priv->duallink_slave,
254				     priv->saved_slave_state);
255}
256
257static int
258sil164_encoder_mode_valid(struct drm_encoder *encoder,
259			  const struct drm_display_mode *mode)
260{
261	struct sil164_priv *priv = to_sil164_priv(encoder);
262
263	if (mode->clock < 32000)
264		return MODE_CLOCK_LOW;
265
266	if (mode->clock > 330000 ||
267	    (mode->clock > 165000 && !priv->duallink_slave))
268		return MODE_CLOCK_HIGH;
269
270	return MODE_OK;
271}
272
273static void
274sil164_encoder_mode_set(struct drm_encoder *encoder,
275			struct drm_display_mode *mode,
276			struct drm_display_mode *adjusted_mode)
277{
278	struct sil164_priv *priv = to_sil164_priv(encoder);
279	bool duallink = adjusted_mode->clock > 165000;
280
281	sil164_init_state(nouveau_i2c_encoder_get_client(encoder),
282			  &priv->config, duallink);
283
284	if (priv->duallink_slave)
285		sil164_init_state(priv->duallink_slave,
286				  &priv->config, duallink);
287
288	sil164_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
289}
290
291static enum drm_connector_status
292sil164_encoder_detect(struct drm_encoder *encoder,
293		      struct drm_connector *connector)
294{
295	struct i2c_client *client = nouveau_i2c_encoder_get_client(encoder);
296
297	if (sil164_read(client, SIL164_DETECT) & SIL164_DETECT_HOTPLUG_STAT)
298		return connector_status_connected;
299	else
300		return connector_status_disconnected;
301}
302
303static int
304sil164_encoder_get_modes(struct drm_encoder *encoder,
305			 struct drm_connector *connector)
306{
307	return 0;
308}
309
310static int
311sil164_encoder_create_resources(struct drm_encoder *encoder,
312				struct drm_connector *connector)
313{
314	return 0;
315}
316
317static int
318sil164_encoder_set_property(struct drm_encoder *encoder,
319			    struct drm_connector *connector,
320			    struct drm_property *property,
321			    uint64_t val)
322{
323	return 0;
324}
325
326static void
327sil164_encoder_destroy(struct drm_encoder *encoder)
328{
329	struct sil164_priv *priv = to_sil164_priv(encoder);
330
331	i2c_unregister_device(priv->duallink_slave);
332
333	kfree(priv);
334	nouveau_i2c_encoder_destroy(encoder);
335}
336
337static const struct nouveau_i2c_encoder_funcs sil164_encoder_funcs = {
338	.set_config = sil164_encoder_set_config,
339	.destroy = sil164_encoder_destroy,
340	.dpms = sil164_encoder_dpms,
341	.save = sil164_encoder_save,
342	.restore = sil164_encoder_restore,
343	.mode_valid = sil164_encoder_mode_valid,
344	.mode_set = sil164_encoder_mode_set,
345	.detect = sil164_encoder_detect,
346	.get_modes = sil164_encoder_get_modes,
347	.create_resources = sil164_encoder_create_resources,
348	.set_property = sil164_encoder_set_property,
349};
350
351/* I2C driver functions */
352
353static int
354sil164_probe(struct i2c_client *client)
355{
356	int vendor = sil164_read(client, SIL164_VENDOR_HI) << 8 |
357		sil164_read(client, SIL164_VENDOR_LO);
358	int device = sil164_read(client, SIL164_DEVICE_HI) << 8 |
359		sil164_read(client, SIL164_DEVICE_LO);
360	int rev = sil164_read(client, SIL164_REVISION);
361
362	if (vendor != 0x1 || device != 0x6) {
363		sil164_dbg(client, "Unknown device %x:%x.%x\n",
364			   vendor, device, rev);
365		return -ENODEV;
366	}
367
368	sil164_info(client, "Detected device %x:%x.%x\n",
369		    vendor, device, rev);
370
371	return 0;
372}
373
374static struct i2c_client *
375sil164_detect_slave(struct i2c_client *client)
376{
377	struct i2c_adapter *adap = client->adapter;
378	struct i2c_msg msg = {
379		.addr = SIL164_I2C_ADDR_SLAVE,
380		.len = 0,
381	};
382	const struct i2c_board_info info = {
383		I2C_BOARD_INFO("sil164", SIL164_I2C_ADDR_SLAVE)
384	};
385
386	if (i2c_transfer(adap, &msg, 1) != 1) {
387		sil164_dbg(adap, "No dual-link slave found.");
388		return NULL;
389	}
390
391	return i2c_new_client_device(adap, &info);
392}
393
394static int
395sil164_encoder_init(struct i2c_client *client,
396		    struct drm_device *dev,
397		    struct nouveau_i2c_encoder *encoder)
398{
399	struct sil164_priv *priv;
400	struct i2c_client *slave_client;
401
402	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
403	if (!priv)
404		return -ENOMEM;
405
406	encoder->encoder_i2c_priv = priv;
407	encoder->encoder_i2c_funcs = &sil164_encoder_funcs;
408
409	slave_client = sil164_detect_slave(client);
410	if (!IS_ERR(slave_client))
411		priv->duallink_slave = slave_client;
412
413	return 0;
414}
415
416static const struct i2c_device_id sil164_ids[] = {
417	{ "sil164" },
418	{ }
419};
420MODULE_DEVICE_TABLE(i2c, sil164_ids);
421
422static struct nouveau_i2c_encoder_driver sil164_driver = {
423	.i2c_driver = {
424		.probe = sil164_probe,
425		.driver = {
426			.name = "sil164",
427		},
428		.id_table = sil164_ids,
429	},
430	.encoder_init = sil164_encoder_init,
431};
432
433/* Module initialization */
434
435static int __init
436sil164_init(void)
437{
438	return i2c_add_driver(&sil164_driver.i2c_driver);
439}
440
441static void __exit
442sil164_exit(void)
443{
444	i2c_del_driver(&sil164_driver.i2c_driver);
445}
446
447MODULE_AUTHOR("Francisco Jerez <currojerez@riseup.net>");
448MODULE_DESCRIPTION("Silicon Image sil164 TMDS transmitter driver");
449MODULE_LICENSE("GPL and additional rights");
450
451module_init(sil164_init);
452module_exit(sil164_exit);
~/ Read-only file preview.