drivers/video/fbdev/goldfishfb.c

Source file repositories/reference/linux-study-clean/drivers/video/fbdev/goldfishfb.c

File Facts

System
Linux kernel
Corpus path
drivers/video/fbdev/goldfishfb.c
Extension
.c
Size
8268 bytes
Lines
318
Domain
Driver Families
Bucket
drivers/video
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct goldfish_fb {
	void __iomem *reg_base;
	int irq;
	spinlock_t lock;
	wait_queue_head_t wait;
	int base_update_count;
	int rotation;
	struct fb_info fb;
	u32 cmap[16];
};

static irqreturn_t goldfish_fb_interrupt(int irq, void *dev_id)
{
	unsigned long irq_flags;
	struct goldfish_fb *fb = dev_id;
	u32 status;

	spin_lock_irqsave(&fb->lock, irq_flags);
	status = readl(fb->reg_base + FB_INT_STATUS);
	if (status & FB_INT_BASE_UPDATE_DONE) {
		fb->base_update_count++;
		wake_up(&fb->wait);
	}
	spin_unlock_irqrestore(&fb->lock, irq_flags);
	return status ? IRQ_HANDLED : IRQ_NONE;
}

static inline u32 convert_bitfield(int val, struct fb_bitfield *bf)
{
	unsigned int mask = (1 << bf->length) - 1;

	return (val >> (16 - bf->length) & mask) << bf->offset;
}

static int
goldfish_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
		 unsigned int blue, unsigned int transp, struct fb_info *info)
{
	struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);

	if (regno < 16) {
		fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) |
				  convert_bitfield(blue, &fb->fb.var.blue) |
				  convert_bitfield(green, &fb->fb.var.green) |
				  convert_bitfield(red, &fb->fb.var.red);
		return 0;
	} else {
		return 1;
	}
}

static int goldfish_fb_check_var(struct fb_var_screeninfo *var,
							struct fb_info *info)
{
	if ((var->rotate & 1) != (info->var.rotate & 1)) {
		if ((var->xres != info->var.yres) ||
				(var->yres != info->var.xres) ||
				(var->xres_virtual != info->var.yres) ||
				(var->yres_virtual > info->var.xres * 2) ||
				(var->yres_virtual < info->var.xres)) {
			return -EINVAL;
		}
	} else {
		if ((var->xres != info->var.xres) ||
		   (var->yres != info->var.yres) ||
		   (var->xres_virtual != info->var.xres) ||
		   (var->yres_virtual > info->var.yres * 2) ||
		   (var->yres_virtual < info->var.yres)) {
			return -EINVAL;
		}
	}
	if ((var->xoffset != info->var.xoffset) ||
			(var->bits_per_pixel != info->var.bits_per_pixel) ||
			(var->grayscale != info->var.grayscale)) {
		return -EINVAL;
	}
	return 0;
}

static int goldfish_fb_set_par(struct fb_info *info)
{
	struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);

	if (fb->rotation != fb->fb.var.rotate) {
		info->fix.line_length = info->var.xres * 2;
		fb->rotation = fb->fb.var.rotate;
		writel(fb->rotation, fb->reg_base + FB_SET_ROTATION);
	}
	return 0;
}

Annotation

Implementation Notes