diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -48,6 +48,15 @@ static struct vc_selection { .start = -1, }; +static bool is_cjk_continuation(const struct vc_data *vc, int offset) +{ + bool continuation; + + vc_uniscr_get_cjk(vc, offset / vc->vc_size_row, + offset % vc->vc_size_row / 2, &continuation); + return continuation; +} + /* clear_selection, highlight and highlight_pointer can be called from interrupt (via scrollback/front) */ @@ -242,6 +251,9 @@ static int vc_selection_store_chars(stru } obp = bp; } + if (IS_ENABLED(CONFIG_FONT_CJK) && i + 2 <= vc_sel.end && + is_cjk_continuation(vc_sel.cons, i + 2)) + i += 2; } vc_sel.buf_len = bp - vc_sel.buffer; diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3021,6 +3021,40 @@ static void vc_con_rewind(struct vc_data #define UCS_VS16 0xfe0f /* Variation Selector 16 */ #define UCS_REPLACEMENT 0xfffd /* Replacement Character */ +#ifdef CONFIG_FONT_CJK +/* Console lock held; zero means that this cell uses the ordinary font. */ +u32 vc_uniscr_get_cjk(const struct vc_data *vc, unsigned int row, + unsigned int col, bool *continuation) +{ + u32 uc; + u16 *p, mask = vc->vc_hi_font_mask | 0xff; + int glyph, charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; + + *continuation = false; + if (!vc->vc_utf || !vc->vc_uni_lines || + row >= vc->vc_rows || col >= vc->vc_cols) + return 0; + uc = vc->vc_uni_lines[row][col]; + p = screenpos(vc, row * vc->vc_size_row + col * 2, false); + if ((vcs_scr_readw(vc, p) & mask) == 0xfe && uc == UCS_ZWS) { + *continuation = true; + if (!col || (vcs_scr_readw(vc, p - 1) & mask) != 0xff) + return UCS_ZWS; + uc = vc->vc_uni_lines[row][col - 1]; + } else if ((vcs_scr_readw(vc, p) & mask) != 0xff) { + return 0; + } + if (uc <= 0xff || uc == UCS_ZWS) + return *continuation ? UCS_ZWS : 0; + glyph = conv_uni_to_pc((struct vc_data *)vc, uc); + if (glyph == -1 || (glyph >= 0 && glyph <= charmask && + glyph < vc->vc_font.charcount)) + return *continuation ? UCS_ZWS : 0; + return uc; +} +EXPORT_SYMBOL_GPL(vc_uniscr_get_cjk); +#endif + static int vc_process_ucs(struct vc_data *vc, int *c, int *tc) { u32 prev_c, curr_c = *c; @@ -3126,6 +3160,7 @@ static int vc_con_write_normal(struct vc u16 himask = vc->vc_hi_font_mask; u8 width = 1; bool inverse = false; + bool cjk = IS_ENABLED(CONFIG_FONT_CJK) && !vc_uniscr_check(vc); if (vc->vc_utf && !vc->vc_disp_ctrl) { width = vc_process_ucs(vc, &c, &tc); @@ -3134,7 +3169,16 @@ static int vc_con_write_normal(struct vc } /* Now try to find out how to display it */ - tc = vc_get_glyph(vc, tc); + if (cjk && c > 0xff) { + tc = conv_uni_to_pc(vc, tc); + cjk = tc < -1 || tc > (himask ? 0x1ff : 0xff) || + tc >= (int)vc->vc_font.charcount; + if (cjk) + tc = 0xff; + } else { + cjk = false; + tc = vc_get_glyph(vc, tc); + } if (tc == -1) return -1; /* nothing to display */ if (tc < 0) { @@ -3148,6 +3192,13 @@ static int vc_con_write_normal(struct vc } next_c = c; + if (IS_ENABLED(CONFIG_FONT_CJK)) { + if (width == 2 && !vc->vc_need_wrap && + vc->state.x == vc->vc_cols - 1) { + vc->vc_need_wrap = vc->vc_decawm; + draw->to = vc->vc_pos + 2; + } + } while (1) { if (vc->vc_need_wrap || vc->vc_decim) con_flush(vc, draw); @@ -3181,10 +3232,14 @@ static int vc_con_write_normal(struct vc if (!--width) break; - /* A space is printed in the second column */ - tc = conv_uni_to_pc(vc, ' '); - if (tc < 0) - tc = ' '; + if (cjk) { + tc = 0xfe; + } else { + /* A space is printed in the second column */ + tc = conv_uni_to_pc(vc, ' '); + if (tc < 0) + tc = ' '; + } /* * Store a zero-width space in the Unicode screen given that * the previous code point is semantically double width. @@ -5052,6 +5107,18 @@ u16 vcs_scr_readw(const struct vc_data * void vcs_scr_writew(struct vc_data *vc, u16 val, u16 *org) { + unsigned long addr = (unsigned long)org; + u16 mask = vc->vc_hi_font_mask | 0xff; + + if (IS_ENABLED(CONFIG_FONT_CJK) && vc->vc_uni_lines && + ((vcs_scr_readw(vc, org) ^ val) & mask) && + addr >= vc->vc_origin && addr < vc->vc_scr_end) { + unsigned int offset = addr - vc->vc_origin; + u16 glyph = (val & 0xff) | ((val & vc->vc_hi_font_mask) ? 0x100 : 0); + + vc->vc_uni_lines[offset / vc->vc_size_row][offset % vc->vc_size_row / 2] = + inverse_translate(vc, glyph, true); + } scr_writew(val, org); if ((unsigned long)org == vc->vc_pos) { softcursor_original = -1; diff --git a/drivers/video/fbdev/core/bitblit.c b/drivers/video/fbdev/core/bitblit.c --- a/drivers/video/fbdev/core/bitblit.c +++ b/drivers/video/fbdev/core/bitblit.c @@ -16,6 +16,9 @@ #include #include #include +#ifdef CONFIG_FONT_CJK +#include +#endif #include #include "fbcon.h" @@ -43,6 +46,88 @@ static void update_attr(u8 *dst, u8 *src } } +#ifdef CONFIG_FONT_CJK +u8 *font_bits(struct vc_data *vc, u16 ch, u32 c_utf, bool continuation, + u32 cellsize, struct fbcon_par *par) +{ + const char *fontname = vc->vc_font.width == 8 && vc->vc_font.height == 16 ? + "CJK16x16" : vc->vc_font.width == 16 && vc->vc_font.height == 32 ? + "CJK32x32" : NULL; + + if (c_utf > 0xffff) + c_utf = 0xfffd; + +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION + if (par) { + static u8 blank[DIV_ROUND_UP(FB_MAX_BLIT_WIDTH, 8) * FB_MAX_BLIT_HEIGHT]; + const struct font_desc *font; + u8 *fontdata; + u32 cellsize_utf, size; + size_t offset; + + if (!par->fontbuffer || par->fd_size < cellsize || + par->fontdata != vc->vc_font.data) + return blank; + if (!c_utf) { + if (ch >= vc->vc_font.charcount || + ((size_t)ch + 1) * cellsize > par->fd_size) + ch = 0; + return par->fontbuffer + ch * cellsize; + } + + font = fontname ? find_font(fontname) : NULL; + if (!font || !font->data) + return par->fontbuffer; + if (font->data == vc->vc_font.data) { + fontdata = par->fontbuffer; + size = par->fd_size; + } else { + if (!par->fontbuffer_utf || par->fontdata_utf != font->data) + return par->fontbuffer; + fontdata = par->fontbuffer_utf; + size = par->fd_size_utf; + } + + cellsize_utf = par->cur_rotate % 2 ? + DIV_ROUND_UP(font->height, 8) * font->width : + DIV_ROUND_UP(font->width, 8) * font->height; + offset = (size_t)c_utf * 2 + continuation; + if (cellsize != cellsize_utf || offset >= font->charcount) + return par->fontbuffer; + offset *= cellsize_utf; + if (offset + cellsize > size) + return par->fontbuffer; + return fontdata + offset; + } else +#endif + { + const struct font_desc *font; + u8 *fontdata; + u32 cellsize_utf; + size_t offset; + + if (!c_utf) { + if (ch >= vc->vc_font.charcount) + ch = 0; + return vc->vc_font.data + ch * cellsize; + } + + font = fontname ? find_font(fontname) : NULL; + if (!font || !font->data) + return vc->vc_font.data; + cellsize_utf = DIV_ROUND_UP(font->width, 8) * font->height; + offset = (size_t)c_utf * 2 + continuation; + if (cellsize != cellsize_utf || offset >= font->charcount) + return vc->vc_font.data; + fontdata = (u8 *)font->data; + offset *= cellsize_utf; + if (offset + cellsize > FNTSIZE(font->data)) + return vc->vc_font.data; + return fontdata + offset; + } +} +#endif + static void bit_bmove(struct vc_data *vc, struct fb_info *info, int sy, int sx, int dy, int dx, int height, int width) { @@ -79,16 +164,31 @@ static inline void bit_putcs_aligned(str struct fb_image *image, u8 *buf, u8 *dst) { u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; - unsigned int charcnt = vc->vc_font.charcount; u32 idx = vc->vc_font.width >> 3; u8 *src; +#ifdef CONFIG_FONT_CJK + struct fbcon_par *par = info->fbcon_par; + u32 y = (image->dy / vc->vc_font.height + par->p->vrows - + par->p->yscroll) % par->p->vrows; + u32 x = image->dx / vc->vc_font.width; +#endif while (cnt--) { - u16 ch = scr_readw(s++) & charmask; - - if (ch >= charcnt) - ch = 0; - src = vc->vc_font.data + (unsigned int)ch * cellsize; +#ifdef CONFIG_FONT_CJK + bool continuation; + u32 c_utf = vc_uniscr_get_cjk(vc, y, x++, &continuation); + + src = font_bits(vc, scr_readw(s++) & charmask, c_utf, + continuation, cellsize, NULL); +#else + { + u16 ch = scr_readw(s++) & charmask; + + if (ch >= vc->vc_font.charcount) + ch = 0; + src = vc->vc_font.data + (unsigned int)ch * cellsize; + } +#endif if (attr) { update_attr(buf, src, attr, vc); @@ -116,18 +216,33 @@ static inline void bit_putcs_unaligned(s u8 *dst) { u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; - unsigned int charcnt = vc->vc_font.charcount; u32 shift_low = 0, mod = vc->vc_font.width % 8; u32 shift_high = 8; u32 idx = vc->vc_font.width >> 3; u8 *src; +#ifdef CONFIG_FONT_CJK + struct fbcon_par *par = info->fbcon_par; + u32 y = (image->dy / vc->vc_font.height + par->p->vrows - + par->p->yscroll) % par->p->vrows; + u32 x = image->dx / vc->vc_font.width; +#endif while (cnt--) { - u16 ch = scr_readw(s++) & charmask; - - if (ch >= charcnt) - ch = 0; - src = vc->vc_font.data + (unsigned int)ch * cellsize; +#ifdef CONFIG_FONT_CJK + bool continuation; + u32 c_utf = vc_uniscr_get_cjk(vc, y, x++, &continuation); + + src = font_bits(vc, scr_readw(s++) & charmask, c_utf, + continuation, cellsize, NULL); +#else + { + u16 ch = scr_readw(s++) & charmask; + + if (ch >= vc->vc_font.charcount) + ch = 0; + src = vc->vc_font.data + (unsigned int)ch * cellsize; + } +#endif if (attr) { update_attr(buf, src, attr, vc); @@ -268,6 +383,10 @@ static void bit_cursor(struct vc_data *v int attribute, use_sw = vc->vc_cursor_type & CUR_SW; int err = 1; char *src; +#ifdef CONFIG_FONT_CJK + bool continuation; + u32 c_utf; +#endif cursor.set = 0; @@ -276,12 +395,19 @@ static void bit_cursor(struct vc_data *v c = scr_readw((u16 *) vc->vc_pos); attribute = get_attribute(info, c); +#ifdef CONFIG_FONT_CJK + c_utf = vc_uniscr_get_cjk(vc, vc->state.y, vc->state.x, &continuation); + + src = font_bits(vc, c & charmask, c_utf, continuation, + w * vc->vc_font.height, NULL); +#else c &= charmask; /* Clamp to font size, same as bit_putcs_aligned() */ if (c >= vc->vc_font.charcount) c = 0; src = vc->vc_font.data + (c * (w * vc->vc_font.height)); +#endif if (par->cursor_state.image.data != src || par->cursor_reset) { diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -726,7 +726,12 @@ static void fbcon_release(struct fb_info kfree(par->cursor_state.mask); kfree(par->cursor_data); kfree(par->cursor_src); +#ifdef CONFIG_FONT_CJK + kvfree(par->fontbuffer); + kvfree(par->fontbuffer_utf); +#else kfree(par->fontbuffer); +#endif kfree(info->fbcon_par); info->fbcon_par = NULL; } @@ -1083,7 +1088,8 @@ static void fbcon_init(struct vc_data *v vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; - if (vc->vc_font.charcount == 256) { + if (IS_ENABLED(CONFIG_FONT_CJK) ? + vc->vc_font.charcount != 512 : vc->vc_font.charcount == 256) { vc->vc_hi_font_mask = 0; } else { vc->vc_hi_font_mask = 0x100; @@ -1405,7 +1411,8 @@ static void fbcon_set_disp(struct fb_inf par->var = info->var; vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; - if (vc->vc_font.charcount == 256) { + if (IS_ENABLED(CONFIG_FONT_CJK) ? + vc->vc_font.charcount != 512 : vc->vc_font.charcount == 256) { vc->vc_hi_font_mask = 0; } else { vc->vc_hi_font_mask = 0x100; @@ -1647,6 +1654,7 @@ static void fbcon_redraw_blit(struct vc_ static void fbcon_redraw(struct vc_data *vc, int line, int count, int offset) { + u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; unsigned short *d = (unsigned short *) (vc->vc_origin + vc->vc_size_row * line); unsigned short *s = d + offset; @@ -1669,7 +1677,10 @@ static void fbcon_redraw(struct vc_data start = s; } } - if (c == scr_readw(d)) { + if ((!IS_ENABLED(CONFIG_FONT_CJK) || + ((c & charmask) != 0xff && + (c & charmask) != 0xfe)) && + c == scr_readw(d)) { if (s > start) { fbcon_putcs(vc, start, s - start, line, x); @@ -2165,7 +2176,8 @@ static bool fbcon_switch(struct vc_data vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; - if (vc->vc_font.charcount > 256) + if (IS_ENABLED(CONFIG_FONT_CJK) ? + vc->vc_font.charcount == 512 : vc->vc_font.charcount > 256) vc->vc_complement_mask <<= 1; updatescrollmode(p, info, vc); diff --git a/drivers/video/fbdev/core/fbcon.h b/drivers/video/fbdev/core/fbcon.h --- a/drivers/video/fbdev/core/fbcon.h +++ b/drivers/video/fbdev/core/fbcon.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -81,10 +82,17 @@ struct fbcon_par { int cur_rotate; char *cursor_data; u8 *fontbuffer; +#ifdef CONFIG_FONT_CJK + u8 *fontbuffer_utf; + const u8 *fontdata_utf; +#endif u8 *fontdata; u8 *cursor_src; u32 cursor_size; u32 fd_size; +#ifdef CONFIG_FONT_CJK + u32 fd_size_utf; +#endif }; /* * Attribute Decoding @@ -187,6 +195,10 @@ extern void fbcon_set_tileops(struct vc_ #endif extern void fbcon_set_bitops(struct fbcon_par *par); extern int soft_cursor(struct fb_info *info, struct fb_cursor *cursor); +#ifdef CONFIG_FONT_CJK +extern u8 *font_bits(struct vc_data *vc, u16 ch, u32 c_utf, bool continuation, + u32 cellsize, struct fbcon_par *par); +#endif #define FBCON_ATTRIBUTE_UNDERLINE 1 #define FBCON_ATTRIBUTE_REVERSE 2 diff --git a/drivers/video/fbdev/core/fbcon_ccw.c b/drivers/video/fbdev/core/fbcon_ccw.c --- a/drivers/video/fbdev/core/fbcon_ccw.c +++ b/drivers/video/fbdev/core/fbcon_ccw.c @@ -103,9 +103,25 @@ static inline void ccw_putcs_aligned(str u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; u32 idx = (vc->vc_font.height + 7) >> 3; u8 *src; +#ifdef CONFIG_FONT_CJK + u32 y = image->dx / vc->vc_font.height; + u32 x = (GETVYRES(par->p, info) - image->dy) / + vc->vc_font.width - 1; + + y = (y + par->p->vrows - par->p->yscroll) % par->p->vrows; +#endif while (cnt--) { - src = par->fontbuffer + (scr_readw(s--) & charmask) * cellsize; +#ifdef CONFIG_FONT_CJK + bool continuation; + u32 c_utf = vc_uniscr_get_cjk(vc, y, x--, &continuation); + + src = font_bits(vc, scr_readw(s--) & charmask, c_utf, + continuation, cellsize, par); +#else + src = par->fontbuffer + + (scr_readw(s--) & charmask) * cellsize; +#endif if (attr) { ccw_update_attr(buf, src, attr, vc); @@ -229,6 +245,10 @@ static void ccw_cursor(struct vc_data *v int err = 1, dx, dy; char *src; u32 vyres = GETVYRES(par->p, info); +#ifdef CONFIG_FONT_CJK + bool continuation; + u32 c_utf; +#endif if (!par->fontbuffer) return; @@ -237,7 +257,15 @@ static void ccw_cursor(struct vc_data *v c = scr_readw((u16 *) vc->vc_pos); attribute = get_attribute(info, c); - src = par->fontbuffer + ((c & charmask) * (w * vc->vc_font.width)); +#ifdef CONFIG_FONT_CJK + c_utf = vc_uniscr_get_cjk(vc, vc->state.y, vc->state.x, &continuation); + + src = font_bits(vc, c & charmask, c_utf, continuation, + w * vc->vc_font.width, par); +#else + src = par->fontbuffer + + ((c & charmask) * (w * vc->vc_font.width)); +#endif if (par->cursor_state.image.data != src || par->cursor_reset) { diff --git a/drivers/video/fbdev/core/fbcon_cw.c b/drivers/video/fbdev/core/fbcon_cw.c --- a/drivers/video/fbdev/core/fbcon_cw.c +++ b/drivers/video/fbdev/core/fbcon_cw.c @@ -88,9 +88,25 @@ static inline void cw_putcs_aligned(stru u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; u32 idx = (vc->vc_font.height + 7) >> 3; u8 *src; +#ifdef CONFIG_FONT_CJK + u32 y = (GETVXRES(par->p, info) - image->dx) / + vc->vc_font.height - 1; + u32 x = image->dy / vc->vc_font.width; + + y = (y + par->p->vrows - par->p->yscroll) % par->p->vrows; +#endif while (cnt--) { - src = par->fontbuffer + (scr_readw(s++) & charmask) * cellsize; +#ifdef CONFIG_FONT_CJK + bool continuation; + u32 c_utf = vc_uniscr_get_cjk(vc, y, x++, &continuation); + + src = font_bits(vc, scr_readw(s++) & charmask, c_utf, + continuation, cellsize, par); +#else + src = par->fontbuffer + + (scr_readw(s++) & charmask) * cellsize; +#endif if (attr) { cw_update_attr(buf, src, attr, vc); @@ -212,6 +228,10 @@ static void cw_cursor(struct vc_data *vc int err = 1, dx, dy; char *src; u32 vxres = GETVXRES(par->p, info); +#ifdef CONFIG_FONT_CJK + bool continuation; + u32 c_utf; +#endif if (!par->fontbuffer) return; @@ -220,7 +240,15 @@ static void cw_cursor(struct vc_data *vc c = scr_readw((u16 *) vc->vc_pos); attribute = get_attribute(info, c); - src = par->fontbuffer + ((c & charmask) * (w * vc->vc_font.width)); +#ifdef CONFIG_FONT_CJK + c_utf = vc_uniscr_get_cjk(vc, vc->state.y, vc->state.x, &continuation); + + src = font_bits(vc, c & charmask, c_utf, continuation, + w * vc->vc_font.width, par); +#else + src = par->fontbuffer + + ((c & charmask) * (w * vc->vc_font.width)); +#endif if (par->cursor_state.image.data != src || par->cursor_reset) { diff --git a/drivers/video/fbdev/core/fbcon_rotate.c b/drivers/video/fbdev/core/fbcon_rotate.c --- a/drivers/video/fbdev/core/fbcon_rotate.c +++ b/drivers/video/fbdev/core/fbcon_rotate.c @@ -14,10 +14,92 @@ #include #include #include +#ifdef CONFIG_FONT_CJK +#include +#include +#endif #include #include "fbcon.h" #include "fbcon_rotate.h" +#ifdef CONFIG_FONT_CJK +static int fbcon_rotate_font_utf(struct fb_info *info, struct vc_data *vc) +{ + struct fbcon_par *par = info->fbcon_par; + const char *fontname = vc->vc_font.width == 8 && vc->vc_font.height == 16 ? + "CJK16x16" : vc->vc_font.width == 16 && vc->vc_font.height == 32 ? + "CJK32x32" : NULL; + const struct font_desc *font = fontname ? find_font(fontname) : NULL; + unsigned int len, s_cellsize, d_cellsize, i; + u32 size; + const u8 *src; + u8 *dst; + int err = 0; + + par->fontdata_utf = NULL; + if (!font || !font->data || font->data == vc->vc_font.data) + goto invalidate; + + src = font->data; + len = font->charcount; + s_cellsize = DIV_ROUND_UP(font->width, 8) * font->height; + d_cellsize = par->rotate % 2 ? + DIV_ROUND_UP(font->height, 8) * font->width : s_cellsize; + if (check_mul_overflow(len, d_cellsize, &size)) { + err = -EOVERFLOW; + goto invalidate; + } + + if (!par->fontbuffer_utf || par->fd_size_utf < size) { + kvfree(par->fontbuffer_utf); + par->fontbuffer_utf = NULL; + par->fd_size_utf = 0; + dst = kvmalloc_array(len, d_cellsize, GFP_KERNEL); + if (!dst) { + err = -ENOMEM; + goto invalidate; + } + par->fd_size_utf = size; + par->fontbuffer_utf = dst; + } + + dst = par->fontbuffer_utf; + memset(dst, 0, size); + switch (par->rotate) { + case FB_ROTATE_UD: + for (i = len; i--; ) { + rotate_ud(src, dst, font->width, font->height); + src += s_cellsize; + dst += d_cellsize; + } + break; + case FB_ROTATE_CW: + for (i = len; i--; ) { + rotate_cw(src, dst, font->width, font->height); + src += s_cellsize; + dst += d_cellsize; + } + break; + case FB_ROTATE_CCW: + for (i = len; i--; ) { + rotate_ccw(src, dst, font->width, font->height); + src += s_cellsize; + dst += d_cellsize; + } + break; + } + par->fontdata_utf = font->data; + return 0; + +invalidate: + kvfree(par->fontbuffer_utf); + par->fontbuffer_utf = NULL; + par->fd_size_utf = 0; + par->fontdata_utf = NULL; + return err; +} +#endif + static int fbcon_rotate_font(struct fb_info *info, struct vc_data *vc) { struct fbcon_par *par = info->fbcon_par; @@ -46,11 +128,19 @@ static int fbcon_rotate_font(struct fb_i info->fbops->fb_sync(info); if (par->fd_size < d_cellsize * len) { +#ifdef CONFIG_FONT_CJK + kvfree(par->fontbuffer); +#else kfree(par->fontbuffer); +#endif par->fontbuffer = NULL; par->fd_size = 0; +#ifdef CONFIG_FONT_CJK + dst = kvmalloc_array(len, d_cellsize, GFP_KERNEL); +#else dst = kmalloc_array(len, d_cellsize, GFP_KERNEL); +#endif if (dst == NULL) { err = -ENOMEM; @@ -92,7 +182,15 @@ static int fbcon_rotate_font(struct fb_i break; } +#ifdef CONFIG_FONT_CJK + err = fbcon_rotate_font_utf(info, vc); +#endif + finished: +#ifdef CONFIG_FONT_CJK + if (err) + par->fontdata = NULL; +#endif return err; } diff --git a/drivers/video/fbdev/core/fbcon_ud.c b/drivers/video/fbdev/core/fbcon_ud.c --- a/drivers/video/fbdev/core/fbcon_ud.c +++ b/drivers/video/fbdev/core/fbcon_ud.c @@ -90,9 +90,26 @@ static inline void ud_putcs_aligned(stru u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; u32 idx = vc->vc_font.width >> 3; u8 *src; +#ifdef CONFIG_FONT_CJK + u32 y = (GETVYRES(par->p, info) - image->dy) / + vc->vc_font.height - 1; + u32 x = (GETVXRES(par->p, info) - image->dx) / + vc->vc_font.width - 1; + + y = (y + par->p->vrows - par->p->yscroll) % par->p->vrows; +#endif while (cnt--) { - src = par->fontbuffer + (scr_readw(s--) & charmask) * cellsize; +#ifdef CONFIG_FONT_CJK + bool continuation; + u32 c_utf = vc_uniscr_get_cjk(vc, y, x--, &continuation); + + src = font_bits(vc, scr_readw(s--) & charmask, c_utf, + continuation, cellsize, par); +#else + src = par->fontbuffer + + (scr_readw(s--) & charmask) * cellsize; +#endif if (attr) { ud_update_attr(buf, src, attr, vc); @@ -125,9 +142,26 @@ static inline void ud_putcs_unaligned(st u32 shift_high = 8; u32 idx = vc->vc_font.width >> 3; u8 *src; +#ifdef CONFIG_FONT_CJK + u32 y = (GETVYRES(par->p, info) - image->dy) / + vc->vc_font.height - 1; + u32 x = (GETVXRES(par->p, info) - image->dx) / + vc->vc_font.width - 1; + + y = (y + par->p->vrows - par->p->yscroll) % par->p->vrows; +#endif while (cnt--) { - src = par->fontbuffer + (scr_readw(s--) & charmask) * cellsize; +#ifdef CONFIG_FONT_CJK + bool continuation; + u32 c_utf = vc_uniscr_get_cjk(vc, y, x--, &continuation); + + src = font_bits(vc, scr_readw(s--) & charmask, c_utf, + continuation, cellsize, par); +#else + src = par->fontbuffer + + (scr_readw(s--) & charmask) * cellsize; +#endif if (attr) { ud_update_attr(buf, src, attr, vc); @@ -260,6 +294,10 @@ static void ud_cursor(struct vc_data *vc char *src; u32 vyres = GETVYRES(par->p, info); u32 vxres = GETVXRES(par->p, info); +#ifdef CONFIG_FONT_CJK + bool continuation; + u32 c_utf; +#endif if (!par->fontbuffer) return; @@ -268,7 +306,15 @@ static void ud_cursor(struct vc_data *vc c = scr_readw((u16 *) vc->vc_pos); attribute = get_attribute(info, c); - src = par->fontbuffer + ((c & charmask) * (w * vc->vc_font.height)); +#ifdef CONFIG_FONT_CJK + c_utf = vc_uniscr_get_cjk(vc, vc->state.y, vc->state.x, &continuation); + + src = font_bits(vc, c & charmask, c_utf, continuation, + w * vc->vc_font.height, par); +#else + src = par->fontbuffer + + ((c & charmask) * (w * vc->vc_font.height)); +#endif if (par->cursor_state.image.data != src || par->cursor_reset) { diff --git a/include/linux/font.h b/include/linux/font.h --- a/include/linux/font.h +++ b/include/linux/font.h @@ -35,6 +35,10 @@ struct font_desc { #define FONT6x10_IDX 10 #define TER16x32_IDX 11 #define FONT6x8_IDX 12 +#ifdef CONFIG_FONT_CJK +#define CJK16x16_IDX 13 +#define CJK32x32_IDX 14 +#endif extern const struct font_desc font_vga_8x8, font_vga_8x16, @@ -49,6 +53,10 @@ extern const struct font_desc font_vga_8 font_6x10, font_ter_16x32, font_6x8; +#ifdef CONFIG_FONT_CJK +extern const struct font_desc font_cjk_16x16, + font_cjk_32x32; +#endif /* Find a font with a specific name */ diff --git a/lib/fonts/Kconfig b/lib/fonts/Kconfig --- a/lib/fonts/Kconfig +++ b/lib/fonts/Kconfig @@ -128,6 +128,40 @@ config FONT_6x8 help This font is useful for small displays (OLED). +config FONT_CJK + bool + +config FONT_CJK_16x16 + bool "CJK 16x16 font" if FONTS + depends on FRAMEBUFFER_CONSOLE && VT_CONSOLE + select FONT_CJK + select CONSOLE_TRANSLATIONS + help + A 16x16 console font covering the Unicode Basic Multilingual Plane, + so the framebuffer console can display Chinese, Japanese and Korean + text. Each glyph occupies two 8x16 cells, and the font data adds + about 2 MiB to the kernel image. + Unicode text uses four bytes per screen cell plus row pointers, + doubled while an alternate screen is saved. Rotation needs 2 MiB per + framebuffer console instance, in addition to the ordinary font. + + If unsure, say N. + +config FONT_CJK_32x32 + bool "CJK 32x32 font" if FONTS + depends on FRAMEBUFFER_CONSOLE && VT_CONSOLE + select FONT_CJK + select CONSOLE_TRANSLATIONS + default n + help + A 32x32 console font covering the Unicode Basic Multilingual Plane, + for screens on which the 16x16 font is too small to read. Each glyph + occupies two 16x32 cells and the data adds about 8 MiB to the kernel + image. Apply cjktty-add-cjk32x32-font-data.patch before enabling this + option; the build fails if font_cjk_32x32.h is empty. + + If unsure, say N. + config FONT_AUTOSELECT def_bool y depends on !FONT_8x8 @@ -142,6 +176,8 @@ config FONT_AUTOSELECT depends on !FONT_10x18 depends on !FONT_TER16x32 depends on !FONT_6x8 + depends on !FONT_CJK_16x16 + depends on !FONT_CJK_32x32 select FONT_8x16 endif # FONT_SUPPORT diff --git a/lib/fonts/Makefile b/lib/fonts/Makefile --- a/lib/fonts/Makefile +++ b/lib/fonts/Makefile @@ -16,6 +16,14 @@ font-objs-$(CONFIG_FONT_MINI_4x6) += fo font-objs-$(CONFIG_FONT_6x10) += font_6x10.o font-objs-$(CONFIG_FONT_TER16x32) += font_ter16x32.o font-objs-$(CONFIG_FONT_6x8) += font_6x8.o +font-objs-$(CONFIG_FONT_CJK_16x16) += font_cjk_16x16.o +font-objs-$(CONFIG_FONT_CJK_32x32) += font_cjk_32x32.o + +ifeq ($(CONFIG_FONT_CJK_32x32),y) +ifeq ($(shell test -s $(srctree)/lib/fonts/font_cjk_32x32.h && echo y),) +$(error CONFIG_FONT_CJK_32x32 requires cjktty-add-cjk32x32-font-data.patch) +endif +endif font-objs += $(font-objs-y) diff --git a/lib/fonts/fonts.c b/lib/fonts/fonts.c --- a/lib/fonts/fonts.c +++ b/lib/fonts/fonts.c @@ -60,6 +60,12 @@ static const struct font_desc *fonts[] = #ifdef CONFIG_FONT_6x8 &font_6x8, #endif +#ifdef CONFIG_FONT_CJK_16x16 + &font_cjk_16x16, +#endif +#ifdef CONFIG_FONT_CJK_32x32 + &font_cjk_32x32, +#endif }; #define num_fonts ARRAY_SIZE(fonts) diff --git a/lib/fonts/font_cjk_16x16.c b/lib/fonts/font_cjk_16x16.c --- a/lib/fonts/font_cjk_16x16.c +++ b/lib/fonts/font_cjk_16x16.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Font data from GNU Unifont 15.1.04. + */ +/***************************************************************/ +/* */ +/* Font file modified from */ +/* http://blog.chinaunix.net/u/13265/showart.php?id=1008020 */ +/* microcaicai@gmail modifiy it to use in-kernel font solution */ +/* */ +/***************************************************************/ + +#include + +#define FONTDATAMAX (65536 * 32) + +static const struct font_data fontdata_cjk_16x16 = { + { 0, 0, FONTDATAMAX, 0 }, { + #include "font_cjk_16x16.h" +}}; + +const struct font_desc font_cjk_16x16 = { + .idx = CJK16x16_IDX, + .name = "CJK16x16", + .width = 8, /* make cursor appear 8 dot width */ + .height = 16, + .charcount = 65536 * 2, + .data = fontdata_cjk_16x16.data, + .pref = 20, /* make it big enough to be chosen */ +}; diff --git a/lib/fonts/font_cjk_32x32.c b/lib/fonts/font_cjk_32x32.c --- a/lib/fonts/font_cjk_32x32.c +++ b/lib/fonts/font_cjk_32x32.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Font data from GNU Unifont 15.1.04; halfwidth glyphs from Terminus. + */ +/***************************************************************/ +/* */ +/* Font file modified from */ +/* http://blog.chinaunix.net/u/13265/showart.php?id=1008020 */ +/* microcaicai@gmail modifiy it to use in-kernel font solution */ +/* */ +/***************************************************************/ + +#include + +#define FONTDATAMAX (65536 * 128) + +static const struct font_data fontdata_cjk_32x32 = { + { 0, 0, FONTDATAMAX, 0 }, { + #include "font_cjk_32x32.h" +}}; + +const struct font_desc font_cjk_32x32 = { + .idx = CJK32x32_IDX, + .name = "CJK32x32", + .width = 16, + .height = 32, + .charcount = 65536 * 2, + .data = fontdata_cjk_32x32.data, + .pref = -1, +}; diff --git a/include/linux/selection.h b/include/linux/selection.h --- a/include/linux/selection.h +++ b/include/linux/selection.h @@ -46,6 +46,17 @@ void vcs_scr_writew(struct vc_data *vc, void vcs_scr_updated(struct vc_data *vc); int vc_uniscr_check(struct vc_data *vc); +#ifdef CONFIG_FONT_CJK +u32 vc_uniscr_get_cjk(const struct vc_data *vc, unsigned int row, + unsigned int col, bool *continuation); +#else +static inline u32 vc_uniscr_get_cjk(const struct vc_data *vc, unsigned int row, + unsigned int col, bool *continuation) +{ + *continuation = false; + return 0; +} +#endif void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed, unsigned int row, unsigned int col, unsigned int nr);