forked from klogg/fl2000_drm
-
Notifications
You must be signed in to change notification settings - Fork 4
/
fl2000_drv.c
165 lines (131 loc) · 4.26 KB
/
fl2000_drv.c
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
// SPDX-License-Identifier: GPL-2.0
/*
* (C) Copyright 2017, Fresco Logic, Incorporated.
* (C) Copyright 2018-2020, Artem Mygaiev
*/
#include <drm/drm_drv.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <drm/drm_managed.h>
#include <drm/drm_modeset_helper.h>
#include "fl2000.h"
#define USB_DRIVER_NAME "fl2000_usb"
#define USB_CLASS_AV 0x10
#define USB_SUBCLASS_AV_CONTROL 0x01
#define USB_SUBCLASS_AV_VIDEO 0x02
#define USB_SUBCLASS_AV_AUDIO 0x03
#define USB_VENDOR_FRESCO_LOGIC 0x1D5C
#define USB_PRODUCT_FL2000 0x2000
static struct usb_driver fl2000_driver;
static int fl2000_probe(struct usb_interface *interface,
const struct usb_device_id *usb_dev_id)
{
int ret = 0;
u8 iface_num = interface->cur_altsetting->desc.bInterfaceNumber;
struct usb_device *usb_dev = interface_to_usbdev(interface);
struct usb_interface *if_stream, *if_interrupt;
struct fl2000 *fl2000_dev;
if (iface_num != FL2000_USBIF_AVCONTROL)
return -ENODEV;
if (usb_dev->speed < USB_SPEED_HIGH) {
dev_err(&usb_dev->dev, "USB 1.1 is not supported!");
return -ENODEV;
}
if (usb_dev->speed == USB_SPEED_HIGH) {
dev_err(&usb_dev->dev, "Using USB 2.0, resolutions may be limited");
}
fl2000_dev = devm_drm_dev_alloc(&usb_dev->dev, &fl2000_drm_driver,
struct fl2000, drm);
if (IS_ERR(fl2000_dev)) {
dev_err(&usb_dev->dev, "Cannot allocate DRM structure (%ld)",
PTR_ERR(fl2000_dev));
return PTR_ERR(fl2000_dev);
}
fl2000_dev->regmap = fl2000_regmap_init(usb_dev);
if (IS_ERR(fl2000_dev->regmap))
return PTR_ERR(fl2000_dev->regmap);
fl2000_dev->adapter = fl2000_i2c_init(usb_dev);
if (IS_ERR(fl2000_dev->adapter))
return PTR_ERR(fl2000_dev->adapter);
dev_set_drvdata(&usb_dev->dev, fl2000_dev);
fl2000_dev->usb_dev = usb_dev;
/* Claim the other interfaces */
fl2000_dev->intf[FL2000_USBIF_AVCONTROL] = interface;
if_stream = usb_ifnum_to_if(usb_dev, FL2000_USBIF_STREAMING);
if (!if_stream) {
dev_err(&usb_dev->dev, "interface %d not found",
FL2000_USBIF_STREAMING);
return -ENXIO;
}
ret = usb_driver_claim_interface(&fl2000_driver, if_stream, fl2000_dev);
if (ret < 0) {
ret = -EBUSY;
}
fl2000_dev->intf[FL2000_USBIF_STREAMING] = if_stream;
if_interrupt = usb_ifnum_to_if(usb_dev, FL2000_USBIF_INTERRUPT);
if (!if_interrupt) {
dev_err(&usb_dev->dev, "interface %d not found",
FL2000_USBIF_INTERRUPT);
ret = -ENXIO;
goto err_unclaim_stream_interface;
}
ret = usb_driver_claim_interface(&fl2000_driver, if_interrupt,
fl2000_dev);
if (ret < 0) {
ret = -EBUSY;
goto err_unclaim_stream_interface;
}
fl2000_dev->intf[FL2000_USBIF_INTERRUPT] = if_interrupt;
ret = fl2000_drm_init(fl2000_dev);
if (ret) {
goto err_unclaim_interrupt_interface;
}
return 0;
err_unclaim_interrupt_interface:
usb_driver_release_interface(&fl2000_driver, if_interrupt);
err_unclaim_stream_interface:
usb_driver_release_interface(&fl2000_driver, if_stream);
return ret;
}
static void fl2000_disconnect(struct usb_interface *interface)
{
struct usb_device *usb_dev = interface_to_usbdev(interface);
struct fl2000 *fl2000_dev = dev_get_drvdata(&usb_dev->dev);
if (!fl2000_dev)
return;
dev_set_drvdata(&usb_dev->dev, NULL);
fl2000_drm_release(fl2000_dev);
}
static int fl2000_suspend(struct usb_interface *interface, pm_message_t message)
{
struct usb_device *usb_dev = interface_to_usbdev(interface);
struct fl2000 *fl2000_dev = dev_get_drvdata(&usb_dev->dev);
return drm_mode_config_helper_suspend(&fl2000_dev->drm);
}
static int fl2000_resume(struct usb_interface *interface)
{
struct usb_device *usb_dev = interface_to_usbdev(interface);
struct fl2000 *fl2000_dev = dev_get_drvdata(&usb_dev->dev);
return drm_mode_config_helper_resume(&fl2000_dev->drm);
}
static struct usb_device_id fl2000_id_table[] = {
{ USB_DEVICE(USB_VENDOR_FRESCO_LOGIC, USB_PRODUCT_FL2000) },
{},
};
MODULE_DEVICE_TABLE(usb, fl2000_id_table);
static struct usb_driver fl2000_driver = {
.name = USB_DRIVER_NAME,
.probe = fl2000_probe,
.disconnect = fl2000_disconnect,
.id_table = fl2000_id_table,
.supports_autosuspend = false,
.disable_hub_initiated_lpm = true,
#ifdef CONFIG_PM
.suspend = fl2000_suspend,
.resume = fl2000_resume,
#endif
};
module_usb_driver(fl2000_driver);
MODULE_AUTHOR("Artem Mygaiev");
MODULE_DESCRIPTION("FL2000 USB display driver");
MODULE_LICENSE("GPL v2");