1*22de25f8SKonrad Dybcio // SPDX-License-Identifier: GPL-2.0-only
2*22de25f8SKonrad Dybcio /*
3*22de25f8SKonrad Dybcio * Copyright (c) 2022 Konrad Dybcio <konrad.dybcio@somainline.org>
4*22de25f8SKonrad Dybcio *
5*22de25f8SKonrad Dybcio * Generated with linux-mdss-dsi-panel-driver-generator with a
6*22de25f8SKonrad Dybcio * substantial amount of manual adjustments.
7*22de25f8SKonrad Dybcio *
8*22de25f8SKonrad Dybcio * SONY Downstream kernel calls this one:
9*22de25f8SKonrad Dybcio * - "JDI ID3" for Akari (XZ2)
10*22de25f8SKonrad Dybcio * - "JDI ID4" for Apollo (XZ2 Compact)
11*22de25f8SKonrad Dybcio */
12*22de25f8SKonrad Dybcio
13*22de25f8SKonrad Dybcio #include <linux/delay.h>
14*22de25f8SKonrad Dybcio #include <linux/gpio/consumer.h>
15*22de25f8SKonrad Dybcio #include <linux/module.h>
16*22de25f8SKonrad Dybcio #include <linux/of.h>
17*22de25f8SKonrad Dybcio #include <linux/regulator/consumer.h>
18*22de25f8SKonrad Dybcio
19*22de25f8SKonrad Dybcio #include <video/mipi_display.h>
20*22de25f8SKonrad Dybcio
21*22de25f8SKonrad Dybcio #include <drm/drm_mipi_dsi.h>
22*22de25f8SKonrad Dybcio #include <drm/drm_modes.h>
23*22de25f8SKonrad Dybcio #include <drm/drm_panel.h>
24*22de25f8SKonrad Dybcio
25*22de25f8SKonrad Dybcio enum {
26*22de25f8SKonrad Dybcio TYPE_TAMA_60HZ,
27*22de25f8SKonrad Dybcio /*
28*22de25f8SKonrad Dybcio * Leaving room for expansion - SONY very often uses
29*22de25f8SKonrad Dybcio * *truly reliably* overclockable panels on their flagships!
30*22de25f8SKonrad Dybcio */
31*22de25f8SKonrad Dybcio };
32*22de25f8SKonrad Dybcio
33*22de25f8SKonrad Dybcio struct sony_td4353_jdi {
34*22de25f8SKonrad Dybcio struct drm_panel panel;
35*22de25f8SKonrad Dybcio struct mipi_dsi_device *dsi;
36*22de25f8SKonrad Dybcio struct regulator_bulk_data supplies[3];
37*22de25f8SKonrad Dybcio struct gpio_desc *panel_reset_gpio;
38*22de25f8SKonrad Dybcio struct gpio_desc *touch_reset_gpio;
39*22de25f8SKonrad Dybcio int type;
40*22de25f8SKonrad Dybcio };
41*22de25f8SKonrad Dybcio
to_sony_td4353_jdi(struct drm_panel * panel)42*22de25f8SKonrad Dybcio static inline struct sony_td4353_jdi *to_sony_td4353_jdi(struct drm_panel *panel)
43*22de25f8SKonrad Dybcio {
44*22de25f8SKonrad Dybcio return container_of(panel, struct sony_td4353_jdi, panel);
45*22de25f8SKonrad Dybcio }
46*22de25f8SKonrad Dybcio
sony_td4353_jdi_on(struct sony_td4353_jdi * ctx)47*22de25f8SKonrad Dybcio static int sony_td4353_jdi_on(struct sony_td4353_jdi *ctx)
48*22de25f8SKonrad Dybcio {
49*22de25f8SKonrad Dybcio struct mipi_dsi_device *dsi = ctx->dsi;
50*22de25f8SKonrad Dybcio struct device *dev = &dsi->dev;
51*22de25f8SKonrad Dybcio int ret;
52*22de25f8SKonrad Dybcio
53*22de25f8SKonrad Dybcio dsi->mode_flags |= MIPI_DSI_MODE_LPM;
54*22de25f8SKonrad Dybcio
55*22de25f8SKonrad Dybcio ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 1080 - 1);
56*22de25f8SKonrad Dybcio if (ret < 0) {
57*22de25f8SKonrad Dybcio dev_err(dev, "Failed to set column address: %d\n", ret);
58*22de25f8SKonrad Dybcio return ret;
59*22de25f8SKonrad Dybcio }
60*22de25f8SKonrad Dybcio
61*22de25f8SKonrad Dybcio ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 2160 - 1);
62*22de25f8SKonrad Dybcio if (ret < 0) {
63*22de25f8SKonrad Dybcio dev_err(dev, "Failed to set page address: %d\n", ret);
64*22de25f8SKonrad Dybcio return ret;
65*22de25f8SKonrad Dybcio }
66*22de25f8SKonrad Dybcio
67*22de25f8SKonrad Dybcio ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0);
68*22de25f8SKonrad Dybcio if (ret < 0) {
69*22de25f8SKonrad Dybcio dev_err(dev, "Failed to set tear scanline: %d\n", ret);
70*22de25f8SKonrad Dybcio return ret;
71*22de25f8SKonrad Dybcio }
72*22de25f8SKonrad Dybcio
73*22de25f8SKonrad Dybcio ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
74*22de25f8SKonrad Dybcio if (ret < 0) {
75*22de25f8SKonrad Dybcio dev_err(dev, "Failed to set tear on: %d\n", ret);
76*22de25f8SKonrad Dybcio return ret;
77*22de25f8SKonrad Dybcio }
78*22de25f8SKonrad Dybcio
79*22de25f8SKonrad Dybcio mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
80*22de25f8SKonrad Dybcio
81*22de25f8SKonrad Dybcio ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
82*22de25f8SKonrad Dybcio if (ret < 0) {
83*22de25f8SKonrad Dybcio dev_err(dev, "Failed to set pixel format: %d\n", ret);
84*22de25f8SKonrad Dybcio return ret;
85*22de25f8SKonrad Dybcio }
86*22de25f8SKonrad Dybcio
87*22de25f8SKonrad Dybcio mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_SET_PARTIAL_ROWS,
88*22de25f8SKonrad Dybcio 0x00, 0x00, 0x08, 0x6f);
89*22de25f8SKonrad Dybcio
90*22de25f8SKonrad Dybcio ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
91*22de25f8SKonrad Dybcio if (ret < 0) {
92*22de25f8SKonrad Dybcio dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
93*22de25f8SKonrad Dybcio return ret;
94*22de25f8SKonrad Dybcio }
95*22de25f8SKonrad Dybcio msleep(70);
96*22de25f8SKonrad Dybcio
97*22de25f8SKonrad Dybcio mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_MEMORY_START);
98*22de25f8SKonrad Dybcio
99*22de25f8SKonrad Dybcio ret = mipi_dsi_dcs_set_display_on(dsi);
100*22de25f8SKonrad Dybcio if (ret < 0) {
101*22de25f8SKonrad Dybcio dev_err(dev, "Failed to turn display on: %d\n", ret);
102*22de25f8SKonrad Dybcio return ret;
103*22de25f8SKonrad Dybcio }
104*22de25f8SKonrad Dybcio
105*22de25f8SKonrad Dybcio return 0;
106*22de25f8SKonrad Dybcio }
107*22de25f8SKonrad Dybcio
sony_td4353_jdi_off(struct sony_td4353_jdi * ctx)108*22de25f8SKonrad Dybcio static int sony_td4353_jdi_off(struct sony_td4353_jdi *ctx)
109*22de25f8SKonrad Dybcio {
110*22de25f8SKonrad Dybcio struct mipi_dsi_device *dsi = ctx->dsi;
111*22de25f8SKonrad Dybcio struct device *dev = &dsi->dev;
112*22de25f8SKonrad Dybcio int ret;
113*22de25f8SKonrad Dybcio
114*22de25f8SKonrad Dybcio dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
115*22de25f8SKonrad Dybcio
116*22de25f8SKonrad Dybcio ret = mipi_dsi_dcs_set_display_off(dsi);
117*22de25f8SKonrad Dybcio if (ret < 0) {
118*22de25f8SKonrad Dybcio dev_err(dev, "Failed to set display off: %d\n", ret);
119*22de25f8SKonrad Dybcio return ret;
120*22de25f8SKonrad Dybcio }
121*22de25f8SKonrad Dybcio msleep(22);
122*22de25f8SKonrad Dybcio
123*22de25f8SKonrad Dybcio ret = mipi_dsi_dcs_set_tear_off(dsi);
124*22de25f8SKonrad Dybcio if (ret < 0) {
125*22de25f8SKonrad Dybcio dev_err(dev, "Failed to set tear off: %d\n", ret);
126*22de25f8SKonrad Dybcio return ret;
127*22de25f8SKonrad Dybcio }
128*22de25f8SKonrad Dybcio
129*22de25f8SKonrad Dybcio ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
130*22de25f8SKonrad Dybcio if (ret < 0) {
131*22de25f8SKonrad Dybcio dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
132*22de25f8SKonrad Dybcio return ret;
133*22de25f8SKonrad Dybcio }
134*22de25f8SKonrad Dybcio msleep(80);
135*22de25f8SKonrad Dybcio
136*22de25f8SKonrad Dybcio return 0;
137*22de25f8SKonrad Dybcio }
138*22de25f8SKonrad Dybcio
sony_td4353_assert_reset_gpios(struct sony_td4353_jdi * ctx,int mode)139*22de25f8SKonrad Dybcio static void sony_td4353_assert_reset_gpios(struct sony_td4353_jdi *ctx, int mode)
140*22de25f8SKonrad Dybcio {
141*22de25f8SKonrad Dybcio gpiod_set_value_cansleep(ctx->touch_reset_gpio, mode);
142*22de25f8SKonrad Dybcio gpiod_set_value_cansleep(ctx->panel_reset_gpio, mode);
143*22de25f8SKonrad Dybcio usleep_range(5000, 5100);
144*22de25f8SKonrad Dybcio }
145*22de25f8SKonrad Dybcio
sony_td4353_jdi_prepare(struct drm_panel * panel)146*22de25f8SKonrad Dybcio static int sony_td4353_jdi_prepare(struct drm_panel *panel)
147*22de25f8SKonrad Dybcio {
148*22de25f8SKonrad Dybcio struct sony_td4353_jdi *ctx = to_sony_td4353_jdi(panel);
149*22de25f8SKonrad Dybcio struct device *dev = &ctx->dsi->dev;
150*22de25f8SKonrad Dybcio int ret;
151*22de25f8SKonrad Dybcio
152*22de25f8SKonrad Dybcio ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
153*22de25f8SKonrad Dybcio if (ret < 0) {
154*22de25f8SKonrad Dybcio dev_err(dev, "Failed to enable regulators: %d\n", ret);
155*22de25f8SKonrad Dybcio return ret;
156*22de25f8SKonrad Dybcio }
157*22de25f8SKonrad Dybcio
158*22de25f8SKonrad Dybcio msleep(100);
159*22de25f8SKonrad Dybcio
160*22de25f8SKonrad Dybcio sony_td4353_assert_reset_gpios(ctx, 1);
161*22de25f8SKonrad Dybcio
162*22de25f8SKonrad Dybcio ret = sony_td4353_jdi_on(ctx);
163*22de25f8SKonrad Dybcio if (ret < 0) {
164*22de25f8SKonrad Dybcio dev_err(dev, "Failed to power on panel: %d\n", ret);
165*22de25f8SKonrad Dybcio sony_td4353_assert_reset_gpios(ctx, 0);
166*22de25f8SKonrad Dybcio regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
167*22de25f8SKonrad Dybcio return ret;
168*22de25f8SKonrad Dybcio }
169*22de25f8SKonrad Dybcio
170*22de25f8SKonrad Dybcio return 0;
171*22de25f8SKonrad Dybcio }
172*22de25f8SKonrad Dybcio
sony_td4353_jdi_unprepare(struct drm_panel * panel)173*22de25f8SKonrad Dybcio static int sony_td4353_jdi_unprepare(struct drm_panel *panel)
174*22de25f8SKonrad Dybcio {
175*22de25f8SKonrad Dybcio struct sony_td4353_jdi *ctx = to_sony_td4353_jdi(panel);
176*22de25f8SKonrad Dybcio struct device *dev = &ctx->dsi->dev;
177*22de25f8SKonrad Dybcio int ret;
178*22de25f8SKonrad Dybcio
179*22de25f8SKonrad Dybcio ret = sony_td4353_jdi_off(ctx);
180*22de25f8SKonrad Dybcio if (ret < 0)
181*22de25f8SKonrad Dybcio dev_err(dev, "Failed to power off panel: %d\n", ret);
182*22de25f8SKonrad Dybcio
183*22de25f8SKonrad Dybcio sony_td4353_assert_reset_gpios(ctx, 0);
184*22de25f8SKonrad Dybcio regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
185*22de25f8SKonrad Dybcio
186*22de25f8SKonrad Dybcio return 0;
187*22de25f8SKonrad Dybcio }
188*22de25f8SKonrad Dybcio
189*22de25f8SKonrad Dybcio static const struct drm_display_mode sony_td4353_jdi_mode_tama_60hz = {
190*22de25f8SKonrad Dybcio .clock = (1080 + 4 + 8 + 8) * (2160 + 259 + 8 + 8) * 60 / 1000,
191*22de25f8SKonrad Dybcio .hdisplay = 1080,
192*22de25f8SKonrad Dybcio .hsync_start = 1080 + 4,
193*22de25f8SKonrad Dybcio .hsync_end = 1080 + 4 + 8,
194*22de25f8SKonrad Dybcio .htotal = 1080 + 4 + 8 + 8,
195*22de25f8SKonrad Dybcio .vdisplay = 2160,
196*22de25f8SKonrad Dybcio .vsync_start = 2160 + 259,
197*22de25f8SKonrad Dybcio .vsync_end = 2160 + 259 + 8,
198*22de25f8SKonrad Dybcio .vtotal = 2160 + 259 + 8 + 8,
199*22de25f8SKonrad Dybcio .width_mm = 64,
200*22de25f8SKonrad Dybcio .height_mm = 128,
201*22de25f8SKonrad Dybcio };
202*22de25f8SKonrad Dybcio
sony_td4353_jdi_get_modes(struct drm_panel * panel,struct drm_connector * connector)203*22de25f8SKonrad Dybcio static int sony_td4353_jdi_get_modes(struct drm_panel *panel,
204*22de25f8SKonrad Dybcio struct drm_connector *connector)
205*22de25f8SKonrad Dybcio {
206*22de25f8SKonrad Dybcio struct sony_td4353_jdi *ctx = to_sony_td4353_jdi(panel);
207*22de25f8SKonrad Dybcio struct drm_display_mode *mode = NULL;
208*22de25f8SKonrad Dybcio
209*22de25f8SKonrad Dybcio if (ctx->type == TYPE_TAMA_60HZ)
210*22de25f8SKonrad Dybcio mode = drm_mode_duplicate(connector->dev, &sony_td4353_jdi_mode_tama_60hz);
211*22de25f8SKonrad Dybcio else
212*22de25f8SKonrad Dybcio return -EINVAL;
213*22de25f8SKonrad Dybcio
214*22de25f8SKonrad Dybcio if (!mode)
215*22de25f8SKonrad Dybcio return -ENOMEM;
216*22de25f8SKonrad Dybcio
217*22de25f8SKonrad Dybcio drm_mode_set_name(mode);
218*22de25f8SKonrad Dybcio
219*22de25f8SKonrad Dybcio mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
220*22de25f8SKonrad Dybcio connector->display_info.width_mm = mode->width_mm;
221*22de25f8SKonrad Dybcio connector->display_info.height_mm = mode->height_mm;
222*22de25f8SKonrad Dybcio drm_mode_probed_add(connector, mode);
223*22de25f8SKonrad Dybcio
224*22de25f8SKonrad Dybcio return 1;
225*22de25f8SKonrad Dybcio }
226*22de25f8SKonrad Dybcio
227*22de25f8SKonrad Dybcio static const struct drm_panel_funcs sony_td4353_jdi_panel_funcs = {
228*22de25f8SKonrad Dybcio .prepare = sony_td4353_jdi_prepare,
229*22de25f8SKonrad Dybcio .unprepare = sony_td4353_jdi_unprepare,
230*22de25f8SKonrad Dybcio .get_modes = sony_td4353_jdi_get_modes,
231*22de25f8SKonrad Dybcio };
232*22de25f8SKonrad Dybcio
sony_td4353_jdi_probe(struct mipi_dsi_device * dsi)233*22de25f8SKonrad Dybcio static int sony_td4353_jdi_probe(struct mipi_dsi_device *dsi)
234*22de25f8SKonrad Dybcio {
235*22de25f8SKonrad Dybcio struct device *dev = &dsi->dev;
236*22de25f8SKonrad Dybcio struct sony_td4353_jdi *ctx;
237*22de25f8SKonrad Dybcio int ret;
238*22de25f8SKonrad Dybcio
239*22de25f8SKonrad Dybcio ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
240*22de25f8SKonrad Dybcio if (!ctx)
241*22de25f8SKonrad Dybcio return -ENOMEM;
242*22de25f8SKonrad Dybcio
243*22de25f8SKonrad Dybcio ctx->type = (uintptr_t)of_device_get_match_data(dev);
244*22de25f8SKonrad Dybcio
245*22de25f8SKonrad Dybcio ctx->supplies[0].supply = "vddio";
246*22de25f8SKonrad Dybcio ctx->supplies[1].supply = "vsp";
247*22de25f8SKonrad Dybcio ctx->supplies[2].supply = "vsn";
248*22de25f8SKonrad Dybcio ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
249*22de25f8SKonrad Dybcio ctx->supplies);
250*22de25f8SKonrad Dybcio if (ret < 0)
251*22de25f8SKonrad Dybcio return dev_err_probe(dev, ret, "Failed to get regulators\n");
252*22de25f8SKonrad Dybcio
253*22de25f8SKonrad Dybcio ctx->panel_reset_gpio = devm_gpiod_get(dev, "panel-reset", GPIOD_ASIS);
254*22de25f8SKonrad Dybcio if (IS_ERR(ctx->panel_reset_gpio))
255*22de25f8SKonrad Dybcio return dev_err_probe(dev, PTR_ERR(ctx->panel_reset_gpio),
256*22de25f8SKonrad Dybcio "Failed to get panel-reset-gpios\n");
257*22de25f8SKonrad Dybcio
258*22de25f8SKonrad Dybcio ctx->touch_reset_gpio = devm_gpiod_get(dev, "touch-reset", GPIOD_ASIS);
259*22de25f8SKonrad Dybcio if (IS_ERR(ctx->touch_reset_gpio))
260*22de25f8SKonrad Dybcio return dev_err_probe(dev, PTR_ERR(ctx->touch_reset_gpio),
261*22de25f8SKonrad Dybcio "Failed to get touch-reset-gpios\n");
262*22de25f8SKonrad Dybcio
263*22de25f8SKonrad Dybcio ctx->dsi = dsi;
264*22de25f8SKonrad Dybcio mipi_dsi_set_drvdata(dsi, ctx);
265*22de25f8SKonrad Dybcio
266*22de25f8SKonrad Dybcio dsi->lanes = 4;
267*22de25f8SKonrad Dybcio dsi->format = MIPI_DSI_FMT_RGB888;
268*22de25f8SKonrad Dybcio dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS;
269*22de25f8SKonrad Dybcio
270*22de25f8SKonrad Dybcio drm_panel_init(&ctx->panel, dev, &sony_td4353_jdi_panel_funcs,
271*22de25f8SKonrad Dybcio DRM_MODE_CONNECTOR_DSI);
272*22de25f8SKonrad Dybcio
273*22de25f8SKonrad Dybcio ret = drm_panel_of_backlight(&ctx->panel);
274*22de25f8SKonrad Dybcio if (ret)
275*22de25f8SKonrad Dybcio return dev_err_probe(dev, ret, "Failed to get backlight\n");
276*22de25f8SKonrad Dybcio
277*22de25f8SKonrad Dybcio drm_panel_add(&ctx->panel);
278*22de25f8SKonrad Dybcio
279*22de25f8SKonrad Dybcio ret = mipi_dsi_attach(dsi);
280*22de25f8SKonrad Dybcio if (ret < 0) {
281*22de25f8SKonrad Dybcio dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
282*22de25f8SKonrad Dybcio drm_panel_remove(&ctx->panel);
283*22de25f8SKonrad Dybcio return ret;
284*22de25f8SKonrad Dybcio }
285*22de25f8SKonrad Dybcio
286*22de25f8SKonrad Dybcio return 0;
287*22de25f8SKonrad Dybcio }
288*22de25f8SKonrad Dybcio
sony_td4353_jdi_remove(struct mipi_dsi_device * dsi)289*22de25f8SKonrad Dybcio static void sony_td4353_jdi_remove(struct mipi_dsi_device *dsi)
290*22de25f8SKonrad Dybcio {
291*22de25f8SKonrad Dybcio struct sony_td4353_jdi *ctx = mipi_dsi_get_drvdata(dsi);
292*22de25f8SKonrad Dybcio int ret;
293*22de25f8SKonrad Dybcio
294*22de25f8SKonrad Dybcio ret = mipi_dsi_detach(dsi);
295*22de25f8SKonrad Dybcio if (ret < 0)
296*22de25f8SKonrad Dybcio dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
297*22de25f8SKonrad Dybcio
298*22de25f8SKonrad Dybcio drm_panel_remove(&ctx->panel);
299*22de25f8SKonrad Dybcio }
300*22de25f8SKonrad Dybcio
301*22de25f8SKonrad Dybcio static const struct of_device_id sony_td4353_jdi_of_match[] = {
302*22de25f8SKonrad Dybcio { .compatible = "sony,td4353-jdi-tama", .data = (void *)TYPE_TAMA_60HZ },
303*22de25f8SKonrad Dybcio { /* sentinel */ }
304*22de25f8SKonrad Dybcio };
305*22de25f8SKonrad Dybcio MODULE_DEVICE_TABLE(of, sony_td4353_jdi_of_match);
306*22de25f8SKonrad Dybcio
307*22de25f8SKonrad Dybcio static struct mipi_dsi_driver sony_td4353_jdi_driver = {
308*22de25f8SKonrad Dybcio .probe = sony_td4353_jdi_probe,
309*22de25f8SKonrad Dybcio .remove = sony_td4353_jdi_remove,
310*22de25f8SKonrad Dybcio .driver = {
311*22de25f8SKonrad Dybcio .name = "panel-sony-td4353-jdi",
312*22de25f8SKonrad Dybcio .of_match_table = sony_td4353_jdi_of_match,
313*22de25f8SKonrad Dybcio },
314*22de25f8SKonrad Dybcio };
315*22de25f8SKonrad Dybcio module_mipi_dsi_driver(sony_td4353_jdi_driver);
316*22de25f8SKonrad Dybcio
317*22de25f8SKonrad Dybcio MODULE_AUTHOR("Konrad Dybcio <konrad.dybcio@somainline.org>");
318*22de25f8SKonrad Dybcio MODULE_DESCRIPTION("DRM panel driver for SONY Xperia XZ2/XZ2c JDI panel");
319*22de25f8SKonrad Dybcio MODULE_LICENSE("GPL");
320