1 /*
2  * Copyright (c) 2018 Loongson Technology Co., Ltd.
3  * Authors:
4  *	Chen Zhu <zhuchen@loongson.cn>
5  *	Yaling Fang <fangyaling@loongson.cn>
6  *	Dandan Zhang <zhangdandan@loongson.cn>
7  *	Huacai Chen <chenhc@lemote.com>
8  * This program is free software; you can redistribute  it and/or modify it
9  * under  the terms of  the GNU General  Public License as published by the
10  * Free Software Foundation;  either version 2 of the  License, or (at your
11  * option) any later version.
12  */
13 
14 #include <linux/export.h>
15 #include <linux/i2c.h>
16 #include <linux/i2c-algo-bit.h>
17 #include <linux/pm_runtime.h>
18 #include <drm/drm_atomic_helper.h>
19 #include <drm/drm_crtc_helper.h>
20 #include <drm/drm_probe_helper.h>
21 #include <drm/drm_edid.h>
22 
23 #include "loongson_drv.h"
24 
25 /**
26  * loongson_connector_best_encoder
27  *
28  * @connector: point to the drm_connector structure
29  *
30  * Select the best encoder for the given connector. Used by both the helpers in
31  * drm_atomic_helper_check_modeset() and legacy CRTC helpers
32  */
loongson_connector_best_encoder(struct drm_connector *connector)33 static struct drm_encoder *loongson_connector_best_encoder(struct drm_connector
34 						  *connector)
35 {
36 	struct drm_encoder *encoder;
37 
38 	/* There is only one encoder per connector */
39 	drm_connector_for_each_possible_encoder(connector, encoder)
40 		return encoder;
41 
42 	return NULL;
43 }
44 
45 /**
46  * loongson_get_modes
47  *
48  * @connetcor: central DRM connector control structure
49  *
50  * Fill in all modes currently valid for the sink into the connector->probed_modes list.
51  * It should also update the EDID property by calling drm_connector_update_edid_property().
52  */
loongson_get_modes(struct drm_connector *connector)53 static int loongson_get_modes(struct drm_connector *connector)
54 {
55 	int id, ret = 0;
56 	enum loongson_edid_method ledid_method;
57 	struct edid *edid = NULL;
58 	struct loongson_connector *lconnector = to_loongson_connector(connector);
59 	struct i2c_adapter *adapter = lconnector->i2c->adapter;
60 
61 	id = drm_connector_index(connector);
62 
63 	ledid_method = lconnector->edid_method;
64 	switch (ledid_method) {
65 	case via_i2c:
66 	case via_encoder:
67 	default:
68 		edid = drm_get_edid(connector, adapter);
69 		break;
70 	case via_vbios:
71 		edid = kmalloc(EDID_LENGTH * 2, GFP_KERNEL);
72 		memcpy(edid, lconnector->vbios_edid, EDID_LENGTH * 2);
73 	}
74 
75 	if (edid) {
76 		drm_connector_update_edid_property(connector, edid);
77 		ret = drm_add_edid_modes(connector, edid);
78 		kfree(edid);
79 	} else {
80 		ret += drm_add_modes_noedid(connector, 1920, 1080);
81 		drm_set_preferred_mode(connector, 1024, 768);
82 	}
83 
84 	return ret;
85 }
86 
is_connected(struct loongson_connector *lconnector)87 static bool is_connected(struct loongson_connector *lconnector)
88 {
89 	unsigned char start = 0x0;
90 	struct i2c_adapter *adapter;
91 	struct i2c_msg msgs = {
92 		.addr = DDC_ADDR,
93 		.len = 1,
94 		.flags = 0,
95 		.buf = &start,
96 	};
97 
98 	if (!lconnector->i2c)
99 		return false;
100 
101 	adapter = lconnector->i2c->adapter;
102 	if (i2c_transfer(adapter, &msgs, 1) < 1) {
103 		DRM_DEBUG_KMS("display-%d not connect\n", lconnector->id);
104 		return false;
105 	}
106 
107 	return true;
108 }
109 
110 /**
111  * loongson_connector_detect
112  *
113  * @connector: point to drm_connector
114  * @force: bool
115  *
116  * Check to see if anything is attached to the connector.
117  * The parameter force is set to false whilst polling,
118  * true when checking the connector due to a user request
119  */
loongson_connector_detect(struct drm_connector *connector, bool force)120 static enum drm_connector_status loongson_connector_detect(struct drm_connector
121 						   *connector, bool force)
122 {
123 	int r;
124 	enum drm_connector_status ret = connector_status_connected;
125 	struct loongson_connector *lconnector = to_loongson_connector(connector);
126 
127 	DRM_DEBUG("loongson_connector_detect connector_id=%d, ledid_method=%d\n",
128 			drm_connector_index(connector), lconnector->edid_method);
129 
130 	if (lconnector->edid_method != via_vbios) {
131 		r = pm_runtime_get_sync(connector->dev->dev);
132 		if (r < 0)
133 			return connector_status_disconnected;
134 
135 		if (is_connected(lconnector))
136 			ret = connector_status_connected;
137 		else
138 			ret = connector_status_disconnected;
139 
140 		pm_runtime_mark_last_busy(connector->dev->dev);
141 		pm_runtime_put_autosuspend(connector->dev->dev);
142 	}
143 
144 	return ret;
145 }
146 
147 /**
148  * These provide the minimum set of functions required to handle a connector
149  *
150  * Helper operations for connectors.These functions are used
151  * by the atomic and legacy modeset helpers and by the probe helpers.
152  */
153 static const struct drm_connector_helper_funcs loongson_connector_helper_funcs = {
154         .get_modes = loongson_get_modes,
155         .best_encoder = loongson_connector_best_encoder,
156 };
157 
158 /**
159  * These provide the minimum set of functions required to handle a connector
160  *
161  * Control connectors on a given device.
162  * The functions below allow the core DRM code to control connectors,
163  * enumerate available modes and so on.
164  */
165 static const struct drm_connector_funcs loongson_connector_funcs = {
166 	.dpms = drm_helper_connector_dpms,
167 	.detect = loongson_connector_detect,
168 	.fill_modes = drm_helper_probe_single_connector_modes,
169 	.destroy = drm_connector_cleanup,
170 	.reset = drm_atomic_helper_connector_reset,
171 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
172 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
173 };
174 
175 static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END };
176 
177 
178 /**
179  * loongson_connector_init
180  *
181  * @dev: drm device
182  * @connector_id:
183  *
184  * Vga is the interface between host and monitor
185  * This function is to init vga
186  */
loongson_connector_init(struct drm_device *dev, unsigned int index)187 struct drm_connector *loongson_connector_init(struct drm_device *dev, unsigned int index)
188 {
189 	struct i2c_adapter *adapter;
190 	struct i2c_client *ddc_client;
191 	struct drm_connector *connector;
192 	struct loongson_encoder *loongson_encoder;
193 	struct loongson_connector *loongson_connector;
194 	struct loongson_drm_device *ldev = (struct loongson_drm_device*)dev->dev_private;
195 
196 	const struct i2c_board_info ddc_info = {
197 		.type = "ddc-dev",
198 		.addr = DDC_ADDR,
199 		.flags = I2C_CLASS_DDC,
200 	};
201 
202 	loongson_encoder = ldev->mode_info[index].encoder;
203 	adapter = loongson_encoder->i2c->adapter;
204 	ddc_client = i2c_new_client_device(adapter, &ddc_info);
205 	if (IS_ERR(ddc_client)) {
206 		i2c_del_adapter(adapter);
207 		DRM_ERROR("Failed to create standard ddc client\n");
208 		return NULL;
209 	}
210 
211 	loongson_connector = kzalloc(sizeof(struct loongson_connector), GFP_KERNEL);
212 	if (!loongson_connector)
213 		return NULL;
214 
215 	ldev->connector_active0 = 0;
216 	ldev->connector_active1 = 0;
217 	loongson_connector->id = index;
218 	loongson_connector->ldev = ldev;
219 	loongson_connector->type = get_connector_type(ldev, index);
220 	loongson_connector->i2c_id = get_connector_i2cid(ldev, index);
221 	loongson_connector->hotplug = get_hotplug_mode(ldev, index);
222 	loongson_connector->edid_method = get_edid_method(ldev, index);
223 	if (loongson_connector->edid_method == via_vbios)
224 		loongson_connector->vbios_edid = get_vbios_edid(ldev, index);
225 
226 	loongson_connector->i2c = &ldev->i2c_bus[index];
227 	if (!loongson_connector->i2c)
228 		DRM_INFO("connector-%d match i2c-%d err\n", index,
229 			 loongson_connector->i2c_id);
230 
231 	connector = &loongson_connector->base;
232 
233 	drm_connector_helper_add(connector, &loongson_connector_helper_funcs);
234 
235 	drm_connector_init(dev, connector,
236 			   &loongson_connector_funcs, loongson_connector->type);
237 
238 	drm_connector_register(connector);
239 
240 	return connector;
241 }
242