Coverage Report

Created: 2026-10-13 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/barebox/drivers/video/fb.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-only
2
#include <common.h>
3
#include <malloc.h>
4
#include <fb.h>
5
#include <errno.h>
6
#include <command.h>
7
#include <getopt.h>
8
#include <fcntl.h>
9
#include <fs.h>
10
#include <init.h>
11
12
DEFINE_DEV_CLASS(fb_class, "fb");
13
EXPORT_SYMBOL(fb_class);
14
15
static int fb_ioctl(struct cdev* cdev, unsigned int req, void *data)
16
0
{
17
0
  struct fb_info *info = cdev->priv;
18
0
  struct fb_info **fb;
19
0
  int ret;
20
21
0
  switch (req) {
22
0
  case FBIOGET_SCREENINFO:
23
0
    fb = data;
24
0
    *fb = info;
25
0
    ret = 0;
26
0
    break;
27
0
  case FBIO_ENABLE:
28
0
    ret = fb_enable(info);
29
0
    break;
30
0
  case FBIO_DISABLE:
31
0
    ret = fb_disable(info);
32
0
    break;
33
0
  default:
34
0
    return -ENOSYS;
35
0
  }
36
37
0
  return ret;
38
0
}
39
40
static int fb_close(struct cdev *cdev)
41
0
{
42
0
  struct fb_info *info = cdev->priv;
43
44
0
  if (info->fbops->fb_flush)
45
0
    info->fbops->fb_flush(info);
46
0
  return 0;
47
0
}
48
49
void fb_damage(struct fb_info *info, struct fb_rect *rect)
50
0
{
51
0
  if (info->fbops->fb_damage)
52
0
    info->fbops->fb_damage(info, rect);
53
0
}
54
55
static int fb_op_flush(struct cdev *cdev)
56
0
{
57
0
  struct fb_info *info = cdev->priv;
58
59
0
  if (info->fbops->fb_flush)
60
0
    info->fbops->fb_flush(info);
61
0
  return 0;
62
0
}
63
64
void fb_flush(struct fb_info *info)
65
0
{
66
0
  if (info->fbops->fb_flush)
67
0
    info->fbops->fb_flush(info);
68
0
}
69
70
static void fb_release_shadowfb(struct fb_info *info)
71
0
{
72
0
  free(info->screen_base_shadow);
73
0
  info->screen_base_shadow = NULL;
74
0
}
75
76
static int fb_alloc_shadowfb(struct fb_info *info)
77
0
{
78
0
  if (info->screen_base_shadow && info->shadowfb)
79
0
    return 0;
80
81
0
  if (!info->screen_base_shadow && !info->shadowfb)
82
0
    return 0;
83
84
0
  if (info->shadowfb) {
85
0
    info->screen_base_shadow = memalign(PAGE_SIZE,
86
0
        info->line_length * info->yres);
87
0
    if (!info->screen_base_shadow)
88
0
      return -ENOMEM;
89
0
    memcpy(info->screen_base_shadow, info->screen_base,
90
0
        info->line_length * info->yres);
91
0
  } else {
92
0
    fb_release_shadowfb(info);
93
0
  }
94
95
0
  return 0;
96
0
}
97
98
int fb_enable(struct fb_info *info)
99
0
{
100
0
  int ret;
101
102
0
  if (info->enabled)
103
0
    return 0;
104
105
0
  ret = fb_alloc_shadowfb(info);
106
0
  if (ret)
107
0
    return ret;
108
109
0
  if (info->fbops->fb_enable)
110
0
    info->fbops->fb_enable(info);
111
112
0
  info->enabled = true;
113
114
0
  return 0;
115
0
}
116
117
int fb_disable(struct fb_info *info)
118
0
{
119
0
  if (!info->enabled)
120
0
    return 0;
121
122
0
  if (info->fbops->fb_disable)
123
0
    info->fbops->fb_disable(info);
124
125
0
  fb_release_shadowfb(info);
126
127
0
  info->enabled = false;
128
129
0
  return 0;
130
0
}
131
132
static int fb_enable_get(struct param_d *param, void *priv)
133
0
{
134
0
  struct fb_info *info = priv;
135
136
0
  info->p_enable = info->enabled;
137
0
  return 0;
138
0
}
139
140
static int fb_enable_set(struct param_d *param, void *priv)
141
0
{
142
0
  struct fb_info *info = priv;
143
144
0
  if (!info->mode && !info->base_plane)
145
0
    return -EINVAL;
146
147
0
  if (info->p_enable)
148
0
    return fb_enable(info);
149
0
  else
150
0
    return fb_disable(info);
151
0
}
152
153
static struct fb_videomode *fb_num_to_mode(struct fb_info *info, int num)
154
0
{
155
0
  int num_modes;
156
157
0
  num_modes = info->modes.num_modes + info->edid_modes.num_modes;
158
159
0
  if (num >= num_modes)
160
0
    return NULL;
161
162
0
  if (num >= info->modes.num_modes)
163
0
    return &info->edid_modes.modes[num - info->modes.num_modes];
164
165
0
  return &info->modes.modes[num];
166
0
}
167
168
static int fb_setup_mode(struct fb_info *info)
169
0
{
170
0
  struct device *dev = &info->dev;
171
0
  int ret;
172
0
  struct fb_videomode *mode;
173
174
0
  if (info->enabled != 0)
175
0
    return -EPERM;
176
177
0
  mode = fb_num_to_mode(info, info->current_mode);
178
0
  if (!mode)
179
0
    return -EINVAL;
180
181
0
  info->mode = mode;
182
183
0
  info->xres = info->mode->xres;
184
0
  info->yres = info->mode->yres;
185
0
  info->line_length = 0;
186
187
0
  if (info->fbops->fb_activate_var) {
188
0
    ret = info->fbops->fb_activate_var(info);
189
0
    if (ret) {
190
0
      info->cdev.size = 0;
191
0
      return ret;
192
0
    }
193
0
  }
194
195
0
  if (!info->line_length)
196
0
    info->line_length = info->xres * (info->bits_per_pixel >> 3);
197
0
  if (!info->screen_size)
198
0
    info->screen_size = info->line_length * info->yres;
199
200
0
  dev->resource[0].start = (resource_size_t)info->screen_base;
201
0
  info->cdev.size = info->line_length * info->yres;
202
0
  dev->resource[0].end = dev->resource[0].start + info->cdev.size - 1;
203
204
0
  return 0;
205
0
}
206
207
static int fb_of_reserve_fixup(struct device_node *root, void *context)
208
0
{
209
0
  struct fb_info *info = context;
210
211
0
  of_del_reserve_entry((unsigned long)info->screen_base,
212
0
           (unsigned long)info->screen_base + info->screen_size);
213
214
0
  if (info->enabled)
215
0
    of_add_reserve_entry((unsigned long)info->screen_base,
216
0
             (unsigned long)info->screen_base + info->screen_size);
217
218
0
  return 0;
219
0
}
220
221
void fb_of_reserve_add_fixup(struct fb_info *info)
222
0
{
223
0
  if (!IS_ENABLED(CONFIG_OFDEVICE))
224
0
    return;
225
226
0
  of_register_fixup(fb_of_reserve_fixup, info);
227
0
}
228
229
static int fb_set_modename(struct param_d *param, void *priv)
230
0
{
231
0
  struct fb_info *info = priv;
232
0
  int ret;
233
234
0
  ret = fb_setup_mode(info);
235
0
  if (ret)
236
0
    return ret;
237
238
0
  return 0;
239
0
}
240
241
static ssize_t fb_cdev_write(struct cdev *cdev, const void *buf, size_t count,
242
           loff_t offset, ulong flags)
243
0
{
244
0
  struct fb_info *info = cdev->priv;
245
0
  ssize_t ret;
246
247
0
  ret = mem_write(cdev, buf, count, offset, flags);
248
0
  if (ret > 0 && info->screen_base_shadow)
249
0
    memcpy(info->screen_base_shadow + offset, buf, ret);
250
251
0
  return ret;
252
0
}
253
254
static struct cdev_operations fb_ops = {
255
  .read = mem_read,
256
  .write  = fb_cdev_write,
257
  .memmap = generic_memmap_rw,
258
  .ioctl  = fb_ioctl,
259
  .close  = fb_close,
260
  .flush  = fb_op_flush,
261
};
262
263
static void fb_print_mode(struct fb_videomode *mode)
264
0
{
265
0
  printf("  %s: %dx%d@%d\n", mode->name,
266
0
      mode->xres, mode->yres, mode->refresh);
267
0
}
268
269
static void fb_print_modes(struct display_timings *modes)
270
0
{
271
0
  int i;
272
273
0
  for (i = 0; i < modes->num_modes; i++)
274
0
    fb_print_mode(&modes->modes[i]);
275
0
}
276
277
static void fb_info(struct device *dev)
278
0
{
279
0
  struct fb_info *info = dev->priv;
280
281
0
  printf("Available modes:\n");
282
0
  fb_print_modes(&info->modes);
283
0
  fb_print_modes(&info->edid_modes);
284
0
  if (info->base_plane) {
285
0
    printf("Type: overlay\n");
286
0
    printf("base plane: %s\n", dev_name(&info->base_plane->dev));
287
0
  } else {
288
0
    printf("Type: primary\n");
289
0
  }
290
0
  printf("xres: %u\n", info->xres);
291
0
  printf("yres: %u\n", info->yres);
292
0
  printf("line_length: %u\n", info->line_length);
293
0
}
294
295
void *fb_get_screen_base(struct fb_info *info)
296
0
{
297
0
  return info->screen_base_shadow ?
298
0
    info->screen_base_shadow : info->screen_base;
299
0
}
300
301
static int fb_set_shadowfb(struct param_d *p, void *priv)
302
0
{
303
0
  struct fb_info *info = priv;
304
305
0
  if (!info->enabled)
306
0
    return 0;
307
308
0
  return fb_alloc_shadowfb(info);
309
0
}
310
311
static const char *mode_name(struct fb_videomode *mode)
312
0
{
313
0
  return basprintf("%dx%d@%d", mode->xres, mode->yres, mode->refresh);
314
0
}
315
316
int register_framebuffer(struct fb_info *info)
317
0
{
318
0
  int id;
319
0
  struct device *dev;
320
0
  int ret, num_modes, i;
321
0
  const char **names;
322
323
0
  dev = &info->dev;
324
325
  /*
326
   * If info->mode is set at this point it's the only mode
327
   * the fb supports. move it over to the modes list.
328
   */
329
0
  if (info->mode) {
330
0
    info->modes.modes = info->mode;
331
0
    info->modes.num_modes = 1;
332
0
  }
333
334
0
  if (!info->line_length)
335
0
    info->line_length = info->xres * (info->bits_per_pixel >> 3);
336
337
0
  info->cdev.ops = &fb_ops;
338
0
  if (info->base_plane) {
339
0
    int ovl_id = info->base_plane->n_overlays++;
340
341
0
    info->cdev.name = basprintf("%s_%d", dev_name(&info->base_plane->dev), ovl_id);
342
0
    dev->id = DEVICE_ID_SINGLE;
343
0
    dev_set_name(dev, "%s", info->cdev.name);
344
0
  } else {
345
0
    id = get_free_deviceid("fb");
346
0
    dev->id = id;
347
0
    dev_set_name(dev, "fb");
348
0
    info->cdev.name = basprintf("fb%d", id);
349
0
  }
350
0
  info->cdev.size = info->line_length * info->yres;
351
0
  info->cdev.dev = dev;
352
0
  info->cdev.priv = info;
353
0
  dev->resource = xzalloc(sizeof(struct resource));
354
0
  dev->resource[0].start = (resource_size_t)info->screen_base;
355
0
  dev->resource[0].end = dev->resource[0].start + info->cdev.size - 1;
356
0
  dev->resource[0].flags = IORESOURCE_MEM;
357
0
  dev->num_resources = 1;
358
359
0
  dev->priv = info;
360
0
  devinfo_add(dev, fb_info);
361
362
0
  ret = register_device(&info->dev);
363
0
  if (ret)
364
0
    goto err_free;
365
366
0
  class_add_device(&fb_class, &info->dev);
367
368
0
  dev_add_param_bool(dev, "enable", fb_enable_set, fb_enable_get,
369
0
      &info->p_enable, info);
370
371
0
  if (IS_ENABLED(CONFIG_DRIVER_VIDEO_EDID))
372
0
    fb_edid_add_modes(info);
373
374
0
  num_modes = info->modes.num_modes + info->edid_modes.num_modes;
375
376
0
  names = xzalloc(sizeof(char *) * num_modes);
377
378
0
  for (i = 0; i < info->modes.num_modes; i++) {
379
0
    if (!info->modes.modes[i].name)
380
0
      info->modes.modes[i].name = mode_name(&info->modes.modes[i]);
381
0
    names[i] = info->modes.modes[i].name;
382
0
  }
383
384
0
  for (i = 0; i < info->edid_modes.num_modes; i++) {
385
0
    if (!info->edid_modes.modes[i].name)
386
0
      info->modes.modes[i].name = mode_name(&info->edid_modes.modes[i]);
387
0
    names[i + info->modes.num_modes] = info->edid_modes.modes[i].name;
388
0
  }
389
390
0
  dev_add_param_enum(dev, "mode_name", fb_set_modename, NULL, &info->current_mode, names, num_modes, info);
391
0
  info->shadowfb = 1;
392
0
  dev_add_param_bool(dev, "shadowfb", fb_set_shadowfb, NULL, &info->shadowfb, info);
393
394
0
  info->mode = fb_num_to_mode(info, 0);
395
396
0
  fb_setup_mode(info);
397
398
0
  ret = devfs_create(&info->cdev);
399
0
  if (ret)
400
0
    goto err_unregister;
401
402
0
  if (IS_ENABLED(CONFIG_DRIVER_VIDEO_SIMPLEFB)) {
403
0
    ret = fb_register_simplefb(info);
404
0
    if (ret)
405
0
      dev_err(&info->dev, "failed to register simplefb: %pe\n",
406
0
          ERR_PTR(ret));
407
0
  }
408
409
0
  if (IS_ENABLED(CONFIG_FRAMEBUFFER_CONSOLE))
410
0
    register_fbconsole(info);
411
412
0
  return 0;
413
414
0
err_unregister:
415
0
  unregister_device(&info->dev);
416
0
err_free:
417
0
  free(dev->resource);
418
419
0
  return ret;
420
0
}