diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
index c852341ec19a..2f7477dc69c1 100644
--- a/drivers/video/fbdev/ps2fb.c
+++ b/drivers/video/fbdev/ps2fb.c
@@ -320,6 +320,47 @@ static u32 var_to_block_count(const struct fb_info *info)
 	return 0;
 }
 
+/**
+ * block_address_for_index - frame buffer block address for given block index
+ * @block_index: index of block to compute the address of
+ * @info: frame buffer info object
+ *
+ * Return: block address, or zero for unsupported pixel storage modes
+ */
+static u32 block_address_for_index(const u32 block_index,
+	const struct fb_info *info)
+{
+	const struct fb_var_screeninfo *var = &info->var;
+	const enum gs_psm psm = var_to_psm(var, info);
+	const u32 fbw = var_to_fbw(var);
+
+	if (psm == gs_psm_ct16)
+		return gs_psm_ct16_block_address(fbw, block_index);
+	if (psm == gs_psm_ct32)
+		return gs_psm_ct32_block_address(fbw, block_index);
+
+	fb_warn_once(info, "%s: Unsupported pixel storage format %u\n",
+		__func__, psm);
+	return 0;
+}
+
+/**
+ * texture_address_for_index - frame buffer texture address for given index
+ * @block_index: index of block to compute the address of
+ * @info: frame buffer info object
+ *
+ * Return: texture address, or zero for unsupported pixel storage modes
+ */
+static u32 texture_address_for_index(const u32 block_index,
+	const struct fb_info *info)
+{
+	const struct ps2fb_par *par = info->par;
+
+	return block_address_for_index(
+		par->cb.block_count + PALETTE_BLOCK_COUNT + block_index,
+		info);
+}
+
 /**
  * texture_least_power_of_2 - round up to a power of 2, not less than 8
  * @n: integer to round up
@@ -357,6 +398,34 @@ static struct cb_tile cb_tile(u32 width, u32 height)
 	};
 }
 
+/**
+ * texture_for_tile - texture base pointer and texel coordinates for tile index
+ * @tile_index: index of tile to compute the texture for
+ * @info: frame buffer info object
+ *
+ * Returns texture base pointer and texel coordinates
+ */
+static struct tile_texture texture_for_tile(const u32 tile_index,
+	const struct fb_info *info)
+{
+	const struct ps2fb_par *par = info->par;
+
+	const u32 texture_tile_count =
+		par->cb.tile.block.cols * par->cb.tile.block.rows;
+	const u32 block_tile = tile_index / texture_tile_count;
+	const u32 texture_tile = tile_index % texture_tile_count;
+	const u32 block_address = texture_address_for_index(block_tile, info);
+
+	const u32 row = texture_tile / par->cb.tile.block.cols;
+	const u32 col = texture_tile % par->cb.tile.block.cols;
+
+	return (struct tile_texture) {
+		.tbp	= block_address,
+		.u	= col * par->cb.tile.width2,
+		.v	= row * par->cb.tile.height2
+	};
+}
+
 /**
  * display_buffer_size - display buffer size for a given video resolution
  *
@@ -560,6 +629,221 @@ void write_cb_environment(struct fb_info *info)
 		fb_err(info, "Failed to write GS environment, GIF is busy\n");
 }
 
+/**
+ * pixel - background or foreground pixel palette index for given coordinates
+ * @image: image to sample pixel from
+ * @x: x coordinate, relative to the top left corner
+ * @y: y coordinate, relative to the top left corner
+ * @info: frame buffer info object
+ *
+ * The background palette index is given for coordinates outside of the image.
+ *
+ * Return: background or foreground palette index
+ */
+static u32 pixel(const struct fb_image * const image,
+	const int x, const int y, struct fb_info *info)
+{
+	if (x < 0 || x >= image->width ||
+	    y < 0 || y >= image->height)
+		return image->bg_color;
+
+	if (image->depth == 1)
+		return (image->data[y*((image->width + 7) >> 3) + (x >> 3)] &
+			(0x80 >> (x & 0x7))) ?
+			image->fg_color : image->bg_color;
+
+	fb_warn_once(info, "%s: Unsupported image depth %u\n",
+		__func__, image->depth);
+	return 0;
+}
+
+/**
+ * ps2fb_cb_texflush - flush texture buffer after palette or texture updates
+ * @info: frame buffer info object
+ *
+ * Before using converted data from host-to-local or local-to-local
+ * transmissions as texture or colour lookup tables for the first time, the
+ * texture buffer must be disabled with the TEXFLUSH register.
+ *
+ * The register write waits for the completion of the current drawing process,
+ * and then disables the texture data read to the texture page buffer. Any
+ * data can be written to it. A drawing process succeeding the write to the
+ * register is started after the texture page buffer is disabled.
+ */
+static void ps2fb_cb_texflush(struct fb_info *info)
+{
+	struct ps2fb_par *par = info->par;
+	union package * const base_package = par->package.buffer;
+	union package *package = base_package;
+	unsigned long flags;
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return;
+
+	spin_lock_irqsave(&par->lock, flags);
+
+        if (!gif_wait())
+		goto timeout;
+
+	GIF_PACKAGE_TAG(package) {
+		.flg = gif_packed_mode,
+		.reg0 = gif_reg_ad,
+		.nreg = 1,
+		.nloop = 1
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_texflush
+	};
+
+	gif_write(&base_package->gif, package - base_package);
+
+timeout:
+	spin_unlock_irqrestore(&par->lock, flags);
+}
+
+/**
+ * package_psmt4_texture - package PSMT4 texture tags and data for the GIF
+ * @package: DMA buffer to put packages in
+ * @image: image to copy
+ * @info: frame buffer info object
+ *
+ * Return: number of generated GIF packages in 16-byte unit
+ */
+static size_t package_psmt4_texture(union package *package,
+	const struct fb_image *image, struct fb_info *info)
+{
+	union package * const base_package = package;
+	const u32 width2 = texture_least_power_of_2(image->width);
+	const u32 height2 = texture_least_power_of_2(image->height);
+	const u32 texels_per_quadword = 32;	/* PSMT4 are 4 bit texels */
+	const u32 nloop = (width2 * height2 + texels_per_quadword - 1) /
+		texels_per_quadword;
+	u32 x, y;
+
+	GIF_PACKAGE_TAG(package) {
+		.flg = gif_image_mode,
+		.nloop = nloop,
+		.eop = 1
+	};
+
+	for (y = 0; y < height2; y++)
+	for (x = 0; x < width2; x += 2) {
+		const int p0 = pixel(image, x + 0, y, info);
+		const int p1 = pixel(image, x + 1, y, info);
+		const int i = 4*y + x/2;
+
+		package[i/16].gif.image[i%16] =
+			(p1 ? 0x10 : 0) | (p0 ? 0x01 : 0);
+	}
+
+	package += nloop;
+
+	return package - base_package;
+}
+
+/**
+ * write_cb_tile - write console buffer tile to the GIF
+ * @tile_index: index of the tile, starting from zero for the first glyph
+ * @image: the image of the tile to write
+ * @info: frame buffer info object
+ */
+static void write_cb_tile(const int tile_index,
+	const struct fb_image *image, struct fb_info *info)
+{
+	struct ps2fb_par *par = info->par;
+	const struct tile_texture tt = texture_for_tile(tile_index, info);
+	union package * const base_package = par->package.buffer;
+	union package *package = base_package;
+
+        if (!gif_wait())
+		return;
+
+	GIF_PACKAGE_TAG(package) {
+		.flg = gif_packed_mode,
+		.reg0 = gif_reg_ad,
+		.nreg = 1,
+		.nloop = 4
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_bitbltbuf,
+		.data.bitbltbuf = {
+			.dpsm = gs_psm_t4,
+			.dbw = GS_PSMT4_BLOCK_WIDTH / 64,
+			.dbp = tt.tbp
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_trxpos,
+		.data.trxpos = {
+			.dsax = tt.u,
+			.dsay = tt.v
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_trxreg,
+		.data.trxreg = {
+			.rrw = texture_least_power_of_2(image->width),
+			.rrh = texture_least_power_of_2(image->height)
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_trxdir,
+		.data.trxdir = { .xdir = gs_trxdir_host_to_local }
+	};
+
+	package += package_psmt4_texture(package, image, info);
+
+	gif_write(&base_package->gif, package - base_package);
+}
+
+/**
+ * ps2fb_cb_settile - set console buffer font tiles
+ * @info: frame buffer info object
+ * @map: font map use as tiles
+*/
+static void ps2fb_cb_settile(struct fb_info *info, struct fb_tilemap *map)
+{
+	const u32 glyph_size = ALIGN(map->width, 8) * map->height / 8;
+	struct ps2fb_par *par = info->par;
+	const u8 *font = map->data;
+	int i;
+
+	if (!font)
+		return;	/* FIXME: Why is fb_settile called with a NULL font? */
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return;
+
+	if (map->width > GS_PSMT4_BLOCK_WIDTH ||
+	    map->height > GS_PSMT4_BLOCK_HEIGHT ||
+	    map->depth != 1) {
+		fb_err(info, "Unsupported font parameters: "
+			"width %d height %d depth %d length %d\n",
+			map->width, map->height, map->depth, map->length);
+		return;
+	}
+
+	par->cb.tile = cb_tile(map->width, map->height);
+
+	for (i = 0; i < map->length; i++) {
+		const struct fb_image image = {
+			.width = map->width,
+			.height = map->height,
+			.fg_color = 1,
+			.bg_color = 0,
+			.depth = 1,
+			.data = &font[i * glyph_size],
+		};
+		unsigned long flags;
+
+		spin_lock_irqsave(&par->lock, flags);
+		write_cb_tile(i, &image, info);
+		spin_unlock_irqrestore(&par->lock, flags);
+	}
+
+	ps2fb_cb_texflush(info);
+}
+
 /**
  * ps2fb_cb_get_tilemax - maximum number of tiles
  * @info: frame buffer info object
@@ -1222,6 +1506,7 @@ static int init_console_buffer(struct platform_device *pdev,
 	};
 
 	static struct fb_tile_ops tileops = {
+		.fb_settile	= ps2fb_cb_settile,
 		.fb_get_tilemax = ps2fb_cb_get_tilemax
 	};
 
