• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/gpu/drm/nouveau/
1#include <linux/pci.h>
2#include <linux/acpi.h>
3#include <linux/slab.h>
4#include <acpi/acpi_drivers.h>
5#include <acpi/acpi_bus.h>
6#include <acpi/video.h>
7
8#include "drmP.h"
9#include "drm.h"
10#include "drm_sarea.h"
11#include "drm_crtc_helper.h"
12#include "nouveau_drv.h"
13#include "nouveau_drm.h"
14#include "nv50_display.h"
15#include "nouveau_connector.h"
16
17#include <linux/vga_switcheroo.h>
18
19#define NOUVEAU_DSM_SUPPORTED 0x00
20#define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00
21
22#define NOUVEAU_DSM_ACTIVE 0x01
23#define NOUVEAU_DSM_ACTIVE_QUERY 0x00
24
25#define NOUVEAU_DSM_LED 0x02
26#define NOUVEAU_DSM_LED_STATE 0x00
27#define NOUVEAU_DSM_LED_OFF 0x10
28#define NOUVEAU_DSM_LED_STAMINA 0x11
29#define NOUVEAU_DSM_LED_SPEED 0x12
30
31#define NOUVEAU_DSM_POWER 0x03
32#define NOUVEAU_DSM_POWER_STATE 0x00
33#define NOUVEAU_DSM_POWER_SPEED 0x01
34#define NOUVEAU_DSM_POWER_STAMINA 0x02
35
36static struct nouveau_dsm_priv {
37	bool dsm_detected;
38	acpi_handle dhandle;
39	acpi_handle rom_handle;
40} nouveau_dsm_priv;
41
42static const char nouveau_dsm_muid[] = {
43	0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
44	0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
45};
46
47static int nouveau_dsm(acpi_handle handle, int func, int arg, uint32_t *result)
48{
49	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
50	struct acpi_object_list input;
51	union acpi_object params[4];
52	union acpi_object *obj;
53	int err;
54
55	input.count = 4;
56	input.pointer = params;
57	params[0].type = ACPI_TYPE_BUFFER;
58	params[0].buffer.length = sizeof(nouveau_dsm_muid);
59	params[0].buffer.pointer = (char *)nouveau_dsm_muid;
60	params[1].type = ACPI_TYPE_INTEGER;
61	params[1].integer.value = 0x00000102;
62	params[2].type = ACPI_TYPE_INTEGER;
63	params[2].integer.value = func;
64	params[3].type = ACPI_TYPE_INTEGER;
65	params[3].integer.value = arg;
66
67	err = acpi_evaluate_object(handle, "_DSM", &input, &output);
68	if (err) {
69		printk(KERN_INFO "failed to evaluate _DSM: %d\n", err);
70		return err;
71	}
72
73	obj = (union acpi_object *)output.pointer;
74
75	if (obj->type == ACPI_TYPE_INTEGER)
76		if (obj->integer.value == 0x80000002)
77			return -ENODEV;
78
79	if (obj->type == ACPI_TYPE_BUFFER) {
80		if (obj->buffer.length == 4 && result) {
81			*result = 0;
82			*result |= obj->buffer.pointer[0];
83			*result |= (obj->buffer.pointer[1] << 8);
84			*result |= (obj->buffer.pointer[2] << 16);
85			*result |= (obj->buffer.pointer[3] << 24);
86		}
87	}
88
89	kfree(output.pointer);
90	return 0;
91}
92
93static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id)
94{
95	return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL);
96}
97
98static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state)
99{
100	int arg;
101	if (state == VGA_SWITCHEROO_ON)
102		arg = NOUVEAU_DSM_POWER_SPEED;
103	else
104		arg = NOUVEAU_DSM_POWER_STAMINA;
105	nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg, NULL);
106	return 0;
107}
108
109static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id)
110{
111	if (id == VGA_SWITCHEROO_IGD)
112		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA);
113	else
114		return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED);
115}
116
117static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id,
118				   enum vga_switcheroo_state state)
119{
120	if (id == VGA_SWITCHEROO_IGD)
121		return 0;
122
123	return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state);
124}
125
126static int nouveau_dsm_init(void)
127{
128	return 0;
129}
130
131static int nouveau_dsm_get_client_id(struct pci_dev *pdev)
132{
133	if (nouveau_dsm_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev))
134		return VGA_SWITCHEROO_IGD;
135	else
136		return VGA_SWITCHEROO_DIS;
137}
138
139static struct vga_switcheroo_handler nouveau_dsm_handler = {
140	.switchto = nouveau_dsm_switchto,
141	.power_state = nouveau_dsm_power_state,
142	.init = nouveau_dsm_init,
143	.get_client_id = nouveau_dsm_get_client_id,
144};
145
146static bool nouveau_dsm_pci_probe(struct pci_dev *pdev)
147{
148	acpi_handle dhandle, nvidia_handle;
149	acpi_status status;
150	int ret;
151	uint32_t result;
152
153	dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
154	if (!dhandle)
155		return false;
156
157	status = acpi_get_handle(dhandle, "_DSM", &nvidia_handle);
158	if (ACPI_FAILURE(status)) {
159		return false;
160	}
161
162	ret = nouveau_dsm(dhandle, NOUVEAU_DSM_SUPPORTED,
163			  NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result);
164	if (ret < 0)
165		return false;
166
167	nouveau_dsm_priv.dhandle = dhandle;
168	return true;
169}
170
171static bool nouveau_dsm_detect(void)
172{
173	char acpi_method_name[255] = { 0 };
174	struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
175	struct pci_dev *pdev = NULL;
176	int has_dsm = 0;
177	int vga_count = 0;
178
179	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
180		vga_count++;
181
182		has_dsm |= (nouveau_dsm_pci_probe(pdev) == true);
183	}
184
185	if (vga_count == 2 && has_dsm) {
186		acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer);
187		printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n",
188		       acpi_method_name);
189		nouveau_dsm_priv.dsm_detected = true;
190		return true;
191	}
192	return false;
193}
194
195void nouveau_register_dsm_handler(void)
196{
197	bool r;
198
199	r = nouveau_dsm_detect();
200	if (!r)
201		return;
202
203	vga_switcheroo_register_handler(&nouveau_dsm_handler);
204}
205
206void nouveau_unregister_dsm_handler(void)
207{
208	vga_switcheroo_unregister_handler();
209}
210
211/* retrieve the ROM in 4k blocks */
212static int nouveau_rom_call(acpi_handle rom_handle, uint8_t *bios,
213			    int offset, int len)
214{
215	acpi_status status;
216	union acpi_object rom_arg_elements[2], *obj;
217	struct acpi_object_list rom_arg;
218	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL};
219
220	rom_arg.count = 2;
221	rom_arg.pointer = &rom_arg_elements[0];
222
223	rom_arg_elements[0].type = ACPI_TYPE_INTEGER;
224	rom_arg_elements[0].integer.value = offset;
225
226	rom_arg_elements[1].type = ACPI_TYPE_INTEGER;
227	rom_arg_elements[1].integer.value = len;
228
229	status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer);
230	if (ACPI_FAILURE(status)) {
231		printk(KERN_INFO "failed to evaluate ROM got %s\n", acpi_format_exception(status));
232		return -ENODEV;
233	}
234	obj = (union acpi_object *)buffer.pointer;
235	memcpy(bios+offset, obj->buffer.pointer, len);
236	kfree(buffer.pointer);
237	return len;
238}
239
240bool nouveau_acpi_rom_supported(struct pci_dev *pdev)
241{
242	acpi_status status;
243	acpi_handle dhandle, rom_handle;
244
245	if (!nouveau_dsm_priv.dsm_detected)
246		return false;
247
248	dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
249	if (!dhandle)
250		return false;
251
252	status = acpi_get_handle(dhandle, "_ROM", &rom_handle);
253	if (ACPI_FAILURE(status))
254		return false;
255
256	nouveau_dsm_priv.rom_handle = rom_handle;
257	return true;
258}
259
260int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len)
261{
262	return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len);
263}
264
265int
266nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
267{
268	struct nouveau_connector *nv_connector = nouveau_connector(connector);
269	struct acpi_device *acpidev;
270	acpi_handle handle;
271	int type, ret;
272	void *edid;
273
274	switch (connector->connector_type) {
275	case DRM_MODE_CONNECTOR_LVDS:
276	case DRM_MODE_CONNECTOR_eDP:
277		type = ACPI_VIDEO_DISPLAY_LCD;
278		break;
279	default:
280		return -EINVAL;
281	}
282
283	handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev);
284	if (!handle)
285		return -ENODEV;
286
287	ret = acpi_bus_get_device(handle, &acpidev);
288	if (ret)
289		return -ENODEV;
290
291	ret = acpi_video_get_edid(acpidev, type, -1, &edid);
292	if (ret < 0)
293		return ret;
294
295	nv_connector->edid = edid;
296	return 0;
297}
298